修復 too many connection 問題
This commit is contained in:
175
lib/emergency-connection-cleanup.ts
Normal file
175
lib/emergency-connection-cleanup.ts
Normal file
@@ -0,0 +1,175 @@
|
||||
// =====================================================
|
||||
// 緊急連線清理工具
|
||||
// =====================================================
|
||||
|
||||
import { db } from './database';
|
||||
import { dbFailover } from './database-failover';
|
||||
import { dbMonitor } from './database-monitor';
|
||||
|
||||
export class EmergencyConnectionCleanup {
|
||||
private static instance: EmergencyConnectionCleanup;
|
||||
|
||||
private constructor() {}
|
||||
|
||||
public static getInstance(): EmergencyConnectionCleanup {
|
||||
if (!EmergencyConnectionCleanup.instance) {
|
||||
EmergencyConnectionCleanup.instance = new EmergencyConnectionCleanup();
|
||||
}
|
||||
return EmergencyConnectionCleanup.instance;
|
||||
}
|
||||
|
||||
// 立即停止所有監控和清理所有連線
|
||||
public async emergencyCleanup() {
|
||||
console.log('🚨 執行緊急連線清理...');
|
||||
|
||||
try {
|
||||
// 1. 立即停止所有監控
|
||||
console.log('⏹️ 停止資料庫監控...');
|
||||
dbMonitor.stopMonitoring();
|
||||
|
||||
// 2. 強制關閉所有連線池
|
||||
console.log('🔌 關閉主要資料庫連線池...');
|
||||
await db.close();
|
||||
|
||||
console.log('🔌 關閉備援資料庫連線池...');
|
||||
await dbFailover.close();
|
||||
|
||||
// 3. 等待一段時間讓連線完全關閉
|
||||
console.log('⏳ 等待連線關閉...');
|
||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||
|
||||
// 4. 檢查連線狀態
|
||||
await this.checkConnectionStatus();
|
||||
|
||||
console.log('✅ 緊急清理完成');
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ 緊急清理失敗:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// 檢查連線狀態(不建立新連線)
|
||||
private async checkConnectionStatus() {
|
||||
try {
|
||||
// 使用一個臨時連線來檢查狀態
|
||||
const tempConnection = await db.getConnection();
|
||||
|
||||
try {
|
||||
const [statusResult] = await tempConnection.execute(`
|
||||
SHOW STATUS LIKE 'Threads_connected'
|
||||
`);
|
||||
|
||||
const [maxConnResult] = await tempConnection.execute(`
|
||||
SHOW VARIABLES LIKE 'max_connections'
|
||||
`);
|
||||
|
||||
const currentConnections = statusResult[0]?.Value || 0;
|
||||
const maxConnections = maxConnResult[0]?.Value || 100;
|
||||
const usagePercentage = (currentConnections / maxConnections) * 100;
|
||||
|
||||
console.log(`📊 清理後連線狀態: ${currentConnections}/${maxConnections} (${usagePercentage.toFixed(1)}%)`);
|
||||
|
||||
if (currentConnections > 5) {
|
||||
console.warn(`⚠️ 仍有 ${currentConnections} 個連線未關閉`);
|
||||
} else {
|
||||
console.log('✅ 連線已成功清理');
|
||||
}
|
||||
|
||||
} finally {
|
||||
tempConnection.release();
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ 檢查連線狀態失敗:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// 強制殺死所有資料庫連線
|
||||
public async forceKillAllConnections() {
|
||||
console.log('💀 強制殺死所有資料庫連線...');
|
||||
|
||||
try {
|
||||
const tempConnection = await db.getConnection();
|
||||
|
||||
try {
|
||||
// 獲取所有連線ID
|
||||
const [connections] = await tempConnection.execute(`
|
||||
SELECT ID FROM information_schema.PROCESSLIST
|
||||
WHERE USER = ? AND COMMAND != 'Sleep'
|
||||
`, [process.env.DB_USER || 'A999']);
|
||||
|
||||
console.log(`🔍 找到 ${connections.length} 個活躍連線`);
|
||||
|
||||
// 殺死所有連線(除了當前連線)
|
||||
for (const conn of connections) {
|
||||
if (conn.ID !== tempConnection.threadId) {
|
||||
try {
|
||||
await tempConnection.execute(`KILL ${conn.ID}`);
|
||||
console.log(`💀 已殺死連線 ${conn.ID}`);
|
||||
} catch (error) {
|
||||
console.log(`⚠️ 無法殺死連線 ${conn.ID}:`, error.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 等待連線關閉
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
|
||||
// 再次檢查狀態
|
||||
await this.checkConnectionStatus();
|
||||
|
||||
} finally {
|
||||
tempConnection.release();
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ 強制殺死連線失敗:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// 獲取連線詳情
|
||||
public async getConnectionDetails() {
|
||||
try {
|
||||
const tempConnection = await db.getConnection();
|
||||
|
||||
try {
|
||||
const [connections] = await tempConnection.execute(`
|
||||
SELECT
|
||||
ID,
|
||||
USER,
|
||||
HOST,
|
||||
DB,
|
||||
COMMAND,
|
||||
TIME,
|
||||
STATE,
|
||||
INFO
|
||||
FROM information_schema.PROCESSLIST
|
||||
WHERE USER = ?
|
||||
ORDER BY TIME DESC
|
||||
`, [process.env.DB_USER || 'A999']);
|
||||
|
||||
console.log('📋 當前資料庫連線詳情:');
|
||||
connections.forEach((conn, index) => {
|
||||
console.log(`${index + 1}. ID: ${conn.ID}, 用戶: ${conn.USER}, 時間: ${conn.TIME}s, 狀態: ${conn.STATE}`);
|
||||
if (conn.INFO && conn.INFO.length > 50) {
|
||||
console.log(` 查詢: ${conn.INFO.substring(0, 50)}...`);
|
||||
} else if (conn.INFO) {
|
||||
console.log(` 查詢: ${conn.INFO}`);
|
||||
}
|
||||
});
|
||||
|
||||
return connections;
|
||||
|
||||
} finally {
|
||||
tempConnection.release();
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ 獲取連線詳情失敗:', error);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 導出單例實例
|
||||
export const emergencyCleanup = EmergencyConnectionCleanup.getInstance();
|
Reference in New Issue
Block a user