Enhance Sequelize configuration and query handling in sequelize.js
- Added connection pool settings to optimize database connection management. - Introduced a queryWithTimeout helper function to handle long-running queries, improving error handling and preventing indefinite hangs. - Updated syncModelsAlways function to utilize queryWithTimeout for foreign key checks and cleanup operations, enhancing robustness and logging for better visibility during synchronization.
This commit is contained in:
@@ -47,8 +47,38 @@ const sequelize = new Sequelize(dbName, dbUser, dbPass, {
|
|||||||
},
|
},
|
||||||
benchmark: SQL_BENCHMARK,
|
benchmark: SQL_BENCHMARK,
|
||||||
logging: sqlLogger,
|
logging: sqlLogger,
|
||||||
|
pool: {
|
||||||
|
max: 20, // Maximale Anzahl von Verbindungen im Pool
|
||||||
|
min: 5, // Minimale Anzahl von Verbindungen im Pool
|
||||||
|
acquire: 60000, // Maximale Zeit (ms) zum Erwerb einer Verbindung (60 Sekunden)
|
||||||
|
idle: 10000, // Maximale Zeit (ms), die eine Verbindung idle sein kann, bevor sie entfernt wird
|
||||||
|
evict: 1000 // Intervall (ms) zum Prüfen auf idle Verbindungen
|
||||||
|
},
|
||||||
|
dialectOptions: {
|
||||||
|
connectTimeout: 60000 // Timeout für Verbindungsaufbau (60 Sekunden)
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Helper: Query mit Timeout (muss nach sequelize Initialisierung definiert werden)
|
||||||
|
const queryWithTimeout = async (query, timeoutMs = 10000, description = 'Query') => {
|
||||||
|
const timeoutPromise = new Promise((_, reject) => {
|
||||||
|
setTimeout(() => reject(new Error(`Timeout after ${timeoutMs}ms`)), timeoutMs);
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
const result = await Promise.race([
|
||||||
|
sequelize.query(query),
|
||||||
|
timeoutPromise
|
||||||
|
]);
|
||||||
|
return result;
|
||||||
|
} catch (error) {
|
||||||
|
if (error.message.includes('Timeout')) {
|
||||||
|
throw error; // Re-throw für bessere Fehlerbehandlung
|
||||||
|
}
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const createSchemas = async () => {
|
const createSchemas = async () => {
|
||||||
await sequelize.query('CREATE SCHEMA IF NOT EXISTS community');
|
await sequelize.query('CREATE SCHEMA IF NOT EXISTS community');
|
||||||
await sequelize.query('CREATE SCHEMA IF NOT EXISTS logs');
|
await sequelize.query('CREATE SCHEMA IF NOT EXISTS logs');
|
||||||
@@ -547,46 +577,57 @@ const syncModelsAlways = async (models) => {
|
|||||||
const schema = model.options?.schema || 'public';
|
const schema = model.options?.schema || 'public';
|
||||||
|
|
||||||
console.log(` 🔍 Checking for foreign keys in ${schema}.${tableName}...`);
|
console.log(` 🔍 Checking for foreign keys in ${schema}.${tableName}...`);
|
||||||
const foreignKeys = await sequelize.query(`
|
// Verwende queryWithTimeout für Foreign Key Queries
|
||||||
|
const foreignKeys = await queryWithTimeout(`
|
||||||
SELECT tc.constraint_name
|
SELECT tc.constraint_name
|
||||||
FROM information_schema.table_constraints AS tc
|
FROM information_schema.table_constraints AS tc
|
||||||
WHERE tc.constraint_type = 'FOREIGN KEY'
|
WHERE tc.constraint_type = 'FOREIGN KEY'
|
||||||
AND tc.table_name = :tableName
|
AND tc.table_name = '${tableName.replace(/'/g, "''")}'
|
||||||
AND tc.table_schema = :schema
|
AND tc.table_schema = '${schema.replace(/'/g, "''")}'
|
||||||
`, {
|
`, 10000, `FK check for ${model.name}`);
|
||||||
replacements: { tableName, schema },
|
|
||||||
type: sequelize.QueryTypes.SELECT
|
|
||||||
});
|
|
||||||
|
|
||||||
if (foreignKeys && foreignKeys.length > 0) {
|
if (foreignKeys && foreignKeys.length > 0) {
|
||||||
console.log(` ⚠️ Found ${foreignKeys.length} existing foreign keys:`, foreignKeys.map(fk => fk.constraint_name).join(', '));
|
console.log(` ⚠️ Found ${foreignKeys.length} existing foreign keys:`, foreignKeys.map(fk => fk.constraint_name).join(', '));
|
||||||
console.log(` ⚠️ Removing ${foreignKeys.length} existing foreign keys from ${model.name} (schema: ${schema}) before sync`);
|
console.log(` ⚠️ Removing ${foreignKeys.length} existing foreign keys from ${model.name} (schema: ${schema}) before sync`);
|
||||||
for (const fk of foreignKeys) {
|
for (const fk of foreignKeys) {
|
||||||
|
try {
|
||||||
console.log(` 🗑️ Dropping constraint: ${fk.constraint_name}`);
|
console.log(` 🗑️ Dropping constraint: ${fk.constraint_name}`);
|
||||||
await sequelize.query(`
|
await queryWithTimeout(`
|
||||||
ALTER TABLE "${schema}"."${tableName}"
|
ALTER TABLE "${schema}"."${tableName}"
|
||||||
DROP CONSTRAINT IF EXISTS "${fk.constraint_name}" CASCADE
|
DROP CONSTRAINT IF EXISTS "${fk.constraint_name}" CASCADE
|
||||||
`);
|
`, 10000, `Drop FK ${fk.constraint_name}`);
|
||||||
|
} catch (dropError) {
|
||||||
|
if (dropError.message.includes('Timeout')) {
|
||||||
|
console.warn(` ⚠️ Timeout beim Entfernen von ${fk.constraint_name} - überspringe...`);
|
||||||
|
} else {
|
||||||
|
console.warn(` ⚠️ Konnte ${fk.constraint_name} nicht entfernen:`, dropError.message?.substring(0, 100));
|
||||||
}
|
}
|
||||||
console.log(` ✅ All foreign keys removed for ${model.name}`);
|
}
|
||||||
|
}
|
||||||
|
console.log(` ✅ Foreign key removal completed for ${model.name}`);
|
||||||
} else {
|
} else {
|
||||||
console.log(` ✅ No foreign keys found for ${model.name}`);
|
console.log(` ✅ No foreign keys found for ${model.name}`);
|
||||||
}
|
}
|
||||||
} catch (fkError) {
|
} catch (fkError) {
|
||||||
console.warn(` ⚠️ Could not remove foreign keys for ${model.name}:`, fkError.message);
|
// Ignoriere Timeout-Fehler - nicht kritisch
|
||||||
console.warn(` ⚠️ Error details:`, fkError);
|
if (fkError.message && fkError.message.includes('Timeout')) {
|
||||||
|
console.warn(` ⚠️ Timeout beim Prüfen der Foreign Keys für ${model.name} - überspringe...`);
|
||||||
|
} else {
|
||||||
|
console.warn(` ⚠️ Could not remove foreign keys for ${model.name}:`, fkError.message?.substring(0, 100));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(` 🔄 Syncing model ${model.name} with constraints: false`);
|
console.log(` 🔄 Syncing model ${model.name} with constraints: false`);
|
||||||
try {
|
try {
|
||||||
// Versuche doppelte pg_description Einträge vor dem Sync zu bereinigen
|
// Versuche doppelte pg_description Einträge vor dem Sync zu bereinigen
|
||||||
// Hinweis: Benötigt Superuser-Rechte oder spezielle Berechtigungen
|
// Hinweis: Benötigt Superuser-Rechte oder spezielle Berechtigungen
|
||||||
|
// Überspringe diese Query, da sie oft fehlschlägt und Verbindungen blockiert
|
||||||
|
// Die doppelten pg_description Einträge sind nicht kritisch für die Funktionalität
|
||||||
try {
|
try {
|
||||||
const tableName = model.tableName;
|
const tableName = model.tableName;
|
||||||
const schema = model.options?.schema || 'public';
|
const schema = model.options?.schema || 'public';
|
||||||
// Verwende direkte Parameter-Einsetzung, da DO $$ keine Parameterbindung unterstützt
|
// Verwende queryWithTimeout mit kurzem Timeout, da diese Query oft hängt
|
||||||
// Die Parameter sind sicher, da sie von Sequelize-Modell-Eigenschaften kommen
|
await queryWithTimeout(`
|
||||||
await sequelize.query(`
|
|
||||||
DELETE FROM pg_catalog.pg_description d1
|
DELETE FROM pg_catalog.pg_description d1
|
||||||
WHERE d1.objoid IN (
|
WHERE d1.objoid IN (
|
||||||
SELECT c.oid
|
SELECT c.oid
|
||||||
@@ -602,13 +643,14 @@ const syncModelsAlways = async (models) => {
|
|||||||
AND d2.objsubid = d1.objsubid
|
AND d2.objsubid = d1.objsubid
|
||||||
AND d2.ctid < d1.ctid
|
AND d2.ctid < d1.ctid
|
||||||
)
|
)
|
||||||
`);
|
`, 5000, `pg_description cleanup for ${model.name}`);
|
||||||
} catch (descError) {
|
} catch (descError) {
|
||||||
// Ignoriere Berechtigungsfehler - das ist normal, wenn der Benutzer keine Superuser-Rechte hat
|
// Ignoriere alle Fehler - diese Query ist optional und blockiert oft
|
||||||
if (descError.message && descError.message.includes('Berechtigung')) {
|
if (descError.message && (descError.message.includes('Berechtigung') || descError.message.includes('timeout') || descError.message.includes('Timeout'))) {
|
||||||
console.log(` ℹ️ Cannot clean up duplicate pg_description entries (requires superuser privileges): ${model.name}`);
|
// Stille Warnung - nicht kritisch
|
||||||
} else {
|
} else {
|
||||||
console.warn(` ⚠️ Could not clean up duplicate pg_description entries for ${model.name}:`, descError.message);
|
// Nur bei unerwarteten Fehlern warnen
|
||||||
|
console.warn(` ⚠️ Could not clean up duplicate pg_description entries for ${model.name}:`, descError.message?.substring(0, 100));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user