diff --git a/backend/utils/sequelize.js b/backend/utils/sequelize.js index 0b36dae..b28a061 100644 --- a/backend/utils/sequelize.js +++ b/backend/utils/sequelize.js @@ -38,6 +38,12 @@ if (!dbName || !dbUser || !dbHost) { throw new Error('Missing required database environment variables: DB_NAME, DB_USER, or DB_HOST'); } +const poolMax = Number.parseInt(process.env.DB_POOL_MAX || '5', 10); +const poolMin = Number.parseInt(process.env.DB_POOL_MIN || '1', 10); +const poolAcquire = Number.parseInt(process.env.DB_POOL_ACQUIRE || '30000', 10); +const poolIdle = Number.parseInt(process.env.DB_POOL_IDLE || '10000', 10); +const poolEvict = Number.parseInt(process.env.DB_POOL_EVICT || '1000', 10); + const sequelize = new Sequelize(dbName, dbUser, dbPass, { host: dbHost, dialect: 'postgres', @@ -48,11 +54,11 @@ const sequelize = new Sequelize(dbName, dbUser, dbPass, { benchmark: SQL_BENCHMARK, logging: sqlLogger, pool: { - max: 5, // Maximale Anzahl von Verbindungen im Pool (reduziert, um Connection Limit zu vermeiden) - min: 1, // Minimale Anzahl von Verbindungen im Pool - acquire: 30000, // Maximale Zeit (ms) zum Erwerb einer Verbindung (30 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 + max: poolMax, // Maximale Anzahl von Verbindungen im Pool + min: poolMin, // Minimale Anzahl von Verbindungen im Pool + acquire: poolAcquire, // Maximale Zeit (ms) zum Erwerb einer Verbindung + idle: poolIdle, // Maximale Zeit (ms), die eine Verbindung idle sein kann, bevor sie entfernt wird + evict: poolEvict, // Intervall (ms) zum Prüfen auf idle Verbindungen handleDisconnects: true // Automatisches Reconnect bei Verbindungsverlust }, dialectOptions: { diff --git a/backend/utils/syncDatabase.js b/backend/utils/syncDatabase.js index 7968f22..8e369d9 100644 --- a/backend/utils/syncDatabase.js +++ b/backend/utils/syncDatabase.js @@ -23,6 +23,27 @@ const queryWithTimeout = async (query, timeoutMs = 30000, description = 'Query') } }; +// Helper: Retry wrapper for transient pool/connection issues +const runWithRetry = async (fn, { retries = 3, delayMs = 2000, description = 'operation' } = {}) => { + let lastError; + for (let attempt = 1; attempt <= retries; attempt++) { + try { + return await fn(); + } catch (error) { + lastError = error; + const isAcquireTimeout = error?.name === 'SequelizeConnectionAcquireTimeoutError' + || error?.message?.includes('ConnectionAcquireTimeoutError') + || error?.message?.includes('Operation timeout'); + if (!isAcquireTimeout || attempt === retries) { + throw error; + } + console.warn(`⚠️ ${description} fehlgeschlagen (AcquireTimeout). Retry ${attempt}/${retries} in ${delayMs}ms...`); + await new Promise(resolve => setTimeout(resolve, delayMs)); + } + } + throw lastError; +}; + // Helper: Prüft ob Tabelle existiert const tableExists = async (schema, tableName) => { try { @@ -592,7 +613,10 @@ const syncDatabase = async () => { await syncModelsWithUpdates(models); console.log("Initializing settings..."); - await initializeSettings(); + await runWithRetry( + () => initializeSettings(), + { retries: 3, delayMs: 2000, description: 'initializeSettings' } + ); console.log("Initializing types..."); await initializeTypes();