// ===================================================== // 資料庫連接配置 (整合備援功能) // ===================================================== import mysql from 'mysql2/promise'; import { dbFailover } from './database-failover'; import { dbSync } from './database-sync'; // 資料庫配置 - 強制快速釋放連線 const dbConfig = { host: process.env.DB_HOST || 'mysql.theaken.com', port: parseInt(process.env.DB_PORT || '33306'), user: process.env.DB_USER || 'AI_Platform', password: process.env.DB_PASSWORD || 'Aa123456', database: process.env.DB_NAME || 'db_AI_Platform', charset: 'utf8mb4', timezone: '+08:00', acquireTimeout: 5000, // 5秒獲取連接超時 timeout: 10000, // 10秒查詢超時 reconnect: true, connectionLimit: 5, // 大幅減少連接數限制 queueLimit: 10, // 減少排隊數量 // 添加連接重試和錯誤處理配置 retryDelay: 1000, maxRetries: 2, // 強制快速釋放連線的配置 idleTimeout: 5000, // 5秒空閒超時,強制快速釋放 maxIdle: 1, // 最多只保留1個空閒連接 // 添加連接生命週期管理 maxReconnects: 3, reconnectDelay: 1000, // 強制關閉空閒連線 keepAliveInitialDelay: 0, enableKeepAlive: false, // 關閉 keepAlive // 添加 SSL 配置(如果需要) ssl: false as any, }; // 創建連接池 const pool = mysql.createPool(dbConfig); // 資料庫連接類 (整合備援功能) export class Database { private static instance: Database; private pool: mysql.Pool; private useFailover: boolean; private constructor() { this.pool = pool; // 強制啟用備援功能,確保系統穩定性 this.useFailover = true; console.log('🔄 資料庫備援功能已啟用'); } public static getInstance(): Database { if (!Database.instance) { Database.instance = new Database(); } return Database.instance; } // 獲取連接 public async getConnection(): Promise { if (this.useFailover) { return await dbFailover.getConnection(); } return await this.pool.getConnection(); } // 執行查詢 public async query(sql: string, params?: any[]): Promise { if (this.useFailover) { return await dbFailover.query(sql, params); } let connection: mysql.PoolConnection | null = null; let retries = 0; const maxRetries = 2; while (retries < maxRetries) { try { connection = await this.getConnection(); const [rows] = await connection.execute(sql, params); return rows as T[]; } catch (error: any) { console.error(`資料庫查詢錯誤 (嘗試 ${retries + 1}/${maxRetries}):`, error.message); if (connection) { try { connection.release(); } catch (releaseError) { console.error('釋放連線時發生錯誤:', releaseError); } connection = null; } if (error.code === 'ECONNRESET' || error.code === 'PROTOCOL_CONNECTION_LOST' || error.code === 'ER_CON_COUNT_ERROR') { retries++; if (retries < maxRetries) { console.log(`等待 ${1000 * retries}ms 後重試...`); await new Promise(resolve => setTimeout(resolve, 1000 * retries)); continue; } } throw error; } finally { if (connection) { try { connection.release(); } catch (releaseError) { console.error('釋放連線時發生錯誤:', releaseError); } } } } throw new Error('資料庫連接失敗,已達到最大重試次數'); } // 執行單一查詢 public async queryOne(sql: string, params?: any[]): Promise { if (this.useFailover) { return await dbFailover.queryOne(sql, params); } try { const results = await this.query(sql, params); return results.length > 0 ? results[0] : null; } catch (error) { console.error('資料庫單一查詢錯誤:', error); throw error; } } // 執行插入(支援雙寫) public async insert(sql: string, params?: any[]): Promise { if (this.useFailover) { // 檢查是否啟用雙寫 const syncStatus = await dbSync.getSyncStatus(); if (syncStatus.enabled) { const result = await dbSync.dualInsert(sql, params); if (result.success) { // 雙寫成功,直接返回成功結果 // 不需要重新執行查詢,因為雙寫已經完成 return { insertId: 0, affectedRows: 1 } as mysql.ResultSetHeader; } else { // 雙寫失敗,拋出錯誤 throw new Error(`雙寫失敗: 主機${result.masterError || '成功'}, 備機${result.slaveError || '成功'}`); } } else { return await dbFailover.insert(sql, params); } } let connection: mysql.PoolConnection | null = null; try { connection = await this.getConnection(); const [result] = await connection.execute(sql, params); return result as mysql.ResultSetHeader; } catch (error) { console.error('資料庫插入錯誤:', error); throw error; } finally { if (connection) { try { connection.release(); } catch (releaseError) { console.error('釋放連線時發生錯誤:', releaseError); } } } } // 執行更新(支援雙寫) public async update(sql: string, params?: any[]): Promise { if (this.useFailover) { // 檢查是否啟用雙寫 const syncStatus = await dbSync.getSyncStatus(); if (syncStatus.enabled) { const result = await dbSync.dualUpdate(sql, params); if (result.success) { // 雙寫成功,直接返回成功結果 // 不需要重新執行查詢,因為雙寫已經完成 return { insertId: 0, affectedRows: 1 } as mysql.ResultSetHeader; } else { // 雙寫失敗,拋出錯誤 throw new Error(`雙寫失敗: 主機${result.masterError || '成功'}, 備機${result.slaveError || '成功'}`); } } else { return await dbFailover.update(sql, params); } } let connection: mysql.PoolConnection | null = null; try { connection = await this.getConnection(); const [result] = await connection.execute(sql, params); return result as mysql.ResultSetHeader; } catch (error) { console.error('資料庫更新錯誤:', error); throw error; } finally { if (connection) { try { connection.release(); } catch (releaseError) { console.error('釋放連線時發生錯誤:', releaseError); } } } } // 執行刪除(支援雙寫) public async delete(sql: string, params?: any[]): Promise { if (this.useFailover) { // 檢查是否啟用雙寫 const syncStatus = await dbSync.getSyncStatus(); if (syncStatus.enabled) { const result = await dbSync.dualDelete(sql, params); if (result.success) { // 雙寫成功,直接返回成功結果 // 不需要重新執行查詢,因為雙寫已經完成 return { insertId: 0, affectedRows: 1 } as mysql.ResultSetHeader; } else { // 雙寫失敗,拋出錯誤 throw new Error(`雙寫失敗: 主機${result.masterError || '成功'}, 備機${result.slaveError || '成功'}`); } } else { return await dbFailover.delete(sql, params); } } let connection: mysql.PoolConnection | null = null; try { connection = await this.getConnection(); const [result] = await connection.execute(sql, params); return result as mysql.ResultSetHeader; } catch (error) { console.error('資料庫刪除錯誤:', error); throw error; } finally { if (connection) { try { connection.release(); } catch (releaseError) { console.error('釋放連線時發生錯誤:', releaseError); } } } } // 開始事務 public async beginTransaction(): Promise { if (this.useFailover) { return await dbFailover.beginTransaction(); } const connection = await this.getConnection(); await connection.beginTransaction(); return connection; } // 提交事務 public async commit(connection: mysql.PoolConnection): Promise { if (this.useFailover) { return await dbFailover.commit(connection); } await connection.commit(); connection.release(); } // 回滾事務 public async rollback(connection: mysql.PoolConnection): Promise { if (this.useFailover) { return await dbFailover.rollback(connection); } await connection.rollback(); connection.release(); } // 獲取備援狀態 public getFailoverStatus() { if (this.useFailover) { return dbFailover.getStatus(); } return null; } // 切換資料庫 public async switchDatabase(database: 'master' | 'slave'): Promise { if (this.useFailover) { return await dbFailover.switchToDatabase(database); } return false; } // 關閉連接池 public async close(): Promise { if (this.useFailover) { await dbFailover.close(); } await this.pool.end(); } } // 導出單例實例 export const db = Database.getInstance(); // 導出類型 export type { PoolConnection } from 'mysql2/promise';