From 586aaec506caf63f4e00c132a46fa9667e3b8e77 Mon Sep 17 00:00:00 2001 From: "Torsten Schulz (local)" Date: Thu, 22 Jan 2026 17:18:27 +0100 Subject: [PATCH] Add queryWithTimeout helper for database operations in syncDatabase.js - Introduced a new helper function to execute database queries with a timeout, improving error handling for long-running queries. - Updated multiple cleanup operations to utilize the new helper, enhancing code readability and maintainability. - Added descriptive logging for each cleanup operation to provide better insights into the database synchronization process. --- backend/utils/syncDatabase.js | 69 +++++++++++++++++++++++------------ 1 file changed, 45 insertions(+), 24 deletions(-) diff --git a/backend/utils/syncDatabase.js b/backend/utils/syncDatabase.js index cc71ff9..ec540eb 100644 --- a/backend/utils/syncDatabase.js +++ b/backend/utils/syncDatabase.js @@ -1,6 +1,27 @@ // syncDatabase.js import { initializeDatabase, syncModelsWithUpdates, syncModelsAlways, sequelize } from './sequelize.js'; + +// Helper: Query mit Timeout +const queryWithTimeout = async (query, timeoutMs = 30000, 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')) { + console.warn(`⚠️ ${description} hat Timeout nach ${timeoutMs}ms - überspringe...`); + return [null, 0]; // Return empty result + } + throw error; + } +}; import initializeTypes from './initializeTypes.js'; import initializeSettings from './initializeSettings.js'; import initializeUserRights from './initializeUserRights.js'; @@ -604,12 +625,12 @@ const syncDatabaseForDeployment = async () => { try { // Cleanup user_param_visibility (optimiert mit LEFT JOIN) console.log(" → Prüfe user_param_visibility..."); - const result1 = await sequelize.query(` + const result1 = await queryWithTimeout(` DELETE FROM community.user_param_visibility WHERE param_id NOT IN ( SELECT id FROM community.user_param ); - `, { timeout: 30000 }); + `, 30000, 'user_param_visibility cleanup'); const deletedCount1 = result1[1] || 0; if (deletedCount1 > 0) { console.log(`✅ ${deletedCount1} verwaiste user_param_visibility Einträge entfernt`); @@ -617,12 +638,12 @@ const syncDatabaseForDeployment = async () => { // Cleanup stock mit ungültigen branch_id (0 oder nicht existierend) console.log(" → Prüfe stock..."); - const result2 = await sequelize.query(` + const result2 = await queryWithTimeout(` DELETE FROM falukant_data.stock WHERE branch_id = 0 OR branch_id NOT IN ( SELECT id FROM falukant_data.branch ); - `, { timeout: 30000 }); + `, 30000, 'stock cleanup'); const deletedCount2 = result2[1] || 0; if (deletedCount2 > 0) { console.log(`✅ ${deletedCount2} verwaiste stock Einträge entfernt`); @@ -630,14 +651,14 @@ const syncDatabaseForDeployment = async () => { // Cleanup knowledge mit ungültigen character_id oder product_id console.log(" → Prüfe knowledge..."); - const result3 = await sequelize.query(` + const result3 = await queryWithTimeout(` DELETE FROM falukant_data.knowledge WHERE character_id NOT IN ( SELECT id FROM falukant_data.character ) OR product_id NOT IN ( SELECT id FROM falukant_type.product ); - `, { timeout: 30000 }); + `, 30000, 'knowledge cleanup'); const deletedCount3 = result3[1] || 0; if (deletedCount3 > 0) { console.log(`✅ ${deletedCount3} verwaiste knowledge Einträge entfernt`); @@ -645,12 +666,12 @@ const syncDatabaseForDeployment = async () => { // Cleanup notification mit ungültigen user_id console.log(" → Prüfe notification..."); - const result4 = await sequelize.query(` + const result4 = await queryWithTimeout(` DELETE FROM falukant_log.notification WHERE user_id NOT IN ( SELECT id FROM falukant_data.falukant_user ); - `, { timeout: 30000 }); + `, 30000, 'notification cleanup'); const deletedCount4 = result4[1] || 0; if (deletedCount4 > 0) { console.log(`✅ ${deletedCount4} verwaiste notification Einträge entfernt`); @@ -658,14 +679,14 @@ const syncDatabaseForDeployment = async () => { // Cleanup promotional_gift mit ungültigen sender_character_id oder recipient_character_id console.log(" → Prüfe promotional_gift..."); - const result5 = await sequelize.query(` + const result5 = await queryWithTimeout(` DELETE FROM falukant_log.promotional_gift WHERE sender_character_id NOT IN ( SELECT id FROM falukant_data.character ) OR recipient_character_id NOT IN ( SELECT id FROM falukant_data.character ); - `, { timeout: 30000 }); + `, 30000, 'promotional_gift cleanup'); const deletedCount5 = result5[1] || 0; if (deletedCount5 > 0) { console.log(`✅ ${deletedCount5} verwaiste promotional_gift Einträge entfernt`); @@ -673,14 +694,14 @@ const syncDatabaseForDeployment = async () => { // Cleanup user_house mit ungültigen house_type_id oder user_id console.log(" → Prüfe user_house..."); - const result6 = await sequelize.query(` + const result6 = await queryWithTimeout(` DELETE FROM falukant_data.user_house WHERE house_type_id NOT IN ( SELECT id FROM falukant_type.house ) OR user_id NOT IN ( SELECT id FROM falukant_data.falukant_user ); - `, { timeout: 30000 }); + `, 30000, 'user_house cleanup'); const deletedCount6 = result6[1] || 0; if (deletedCount6 > 0) { console.log(`✅ ${deletedCount6} verwaiste user_house Einträge entfernt`); @@ -688,7 +709,7 @@ const syncDatabaseForDeployment = async () => { // Cleanup child_relation mit ungültigen father_character_id, mother_character_id oder child_character_id console.log(" → Prüfe child_relation..."); - const result7 = await sequelize.query(` + const result7 = await queryWithTimeout(` DELETE FROM falukant_data.child_relation WHERE father_character_id NOT IN ( SELECT id FROM falukant_data.character @@ -697,7 +718,7 @@ const syncDatabaseForDeployment = async () => { ) OR child_character_id NOT IN ( SELECT id FROM falukant_data.character ); - `, { timeout: 30000 }); + `, 30000, 'child_relation cleanup'); const deletedCount7 = result7[1] || 0; if (deletedCount7 > 0) { console.log(`✅ ${deletedCount7} verwaiste child_relation Einträge entfernt`); @@ -705,7 +726,7 @@ const syncDatabaseForDeployment = async () => { // Cleanup political_office mit ungültigen character_id, office_type_id oder region_id console.log(" → Prüfe political_office..."); - const result8 = await sequelize.query(` + const result8 = await queryWithTimeout(` DELETE FROM falukant_data.political_office WHERE character_id NOT IN ( SELECT id FROM falukant_data.character @@ -714,7 +735,7 @@ const syncDatabaseForDeployment = async () => { ) OR region_id NOT IN ( SELECT id FROM falukant_data.region ); - `, { timeout: 30000 }); + `, 30000, 'political_office cleanup'); const deletedCount8 = result8[1] || 0; if (deletedCount8 > 0) { console.log(`✅ ${deletedCount8} verwaiste political_office Einträge entfernt`); @@ -722,7 +743,7 @@ const syncDatabaseForDeployment = async () => { // Cleanup church_office mit ungültigen character_id, office_type_id oder region_id console.log(" → Prüfe church_office..."); - const result11 = await sequelize.query(` + const result11 = await queryWithTimeout(` DELETE FROM falukant_data.church_office WHERE character_id NOT IN ( SELECT id FROM falukant_data.character @@ -731,7 +752,7 @@ const syncDatabaseForDeployment = async () => { ) OR region_id NOT IN ( SELECT id FROM falukant_data.region ); - `, { timeout: 30000 }); + `, 30000, 'church_office cleanup'); const deletedCount11 = result11[1] || 0; if (deletedCount11 > 0) { console.log(`✅ ${deletedCount11} verwaiste church_office Einträge entfernt`); @@ -739,7 +760,7 @@ const syncDatabaseForDeployment = async () => { // Cleanup church_application mit ungültigen character_id, office_type_id, region_id oder supervisor_id console.log(" → Prüfe church_application..."); - const result12 = await sequelize.query(` + const result12 = await queryWithTimeout(` DELETE FROM falukant_data.church_application WHERE character_id NOT IN ( SELECT id FROM falukant_data.character @@ -750,7 +771,7 @@ const syncDatabaseForDeployment = async () => { ) OR supervisor_id NOT IN ( SELECT id FROM falukant_data.character ); - `, { timeout: 30000 }); + `, 30000, 'church_application cleanup'); const deletedCount12 = result12[1] || 0; if (deletedCount12 > 0) { console.log(`✅ ${deletedCount12} verwaiste church_application Einträge entfernt`); @@ -758,20 +779,20 @@ const syncDatabaseForDeployment = async () => { // Cleanup vehicle.condition: Legacy-Nulls + Range clamp (UI zeigt sonst "Unbekannt") console.log(" → Prüfe vehicle.condition..."); - const result9 = await sequelize.query(` + const result9 = await queryWithTimeout(` UPDATE falukant_data.vehicle SET condition = 100 WHERE condition IS NULL; - `, { timeout: 30000 }); + `, 30000, 'vehicle condition NULL update'); const updatedNullConditions = result9[1] || 0; if (updatedNullConditions > 0) { console.log(`✅ ${updatedNullConditions} vehicle.condition NULL → 100 gesetzt`); } - const result10 = await sequelize.query(` + const result10 = await queryWithTimeout(` UPDATE falukant_data.vehicle SET condition = GREATEST(0, LEAST(100, condition)) WHERE condition < 0 OR condition > 100; - `, { timeout: 30000 }); + `, 30000, 'vehicle condition clamp'); const clampedConditions = result10[1] || 0; if (clampedConditions > 0) { console.log(`✅ ${clampedConditions} vehicle.condition Werte auf 0..100 geklemmt`);