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:
Torsten Schulz (local)
2026-01-23 12:48:26 +01:00
parent 88967ba9d3
commit c1cda5fa62

View File

@@ -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));
} }
} }