162 lines
4.5 KiB
TypeScript
162 lines
4.5 KiB
TypeScript
// =====================================================
|
|
// 資料庫連線管理中間件
|
|
// =====================================================
|
|
|
|
import { NextRequest, NextResponse } from 'next/server';
|
|
import { db } from './database';
|
|
import { dbMonitor } from './database-monitor';
|
|
import { dbShutdownManager } from './database-shutdown-manager';
|
|
import { connectionMonitor } from './connection-monitor';
|
|
|
|
// 連線池狀態追蹤
|
|
let connectionCount = 0;
|
|
const maxConnections = 20; // 與資料庫配置保持一致
|
|
|
|
export function withDatabaseConnection<T extends any[]>(
|
|
handler: (...args: T) => Promise<NextResponse>
|
|
) {
|
|
return async (...args: T): Promise<NextResponse> => {
|
|
// 檢查連線池狀態
|
|
if (connectionCount >= maxConnections) {
|
|
console.warn(`⚠️ 連線池已滿 (${connectionCount}/${maxConnections}),等待可用連線...`);
|
|
|
|
// 等待一段時間後重試
|
|
await new Promise(resolve => setTimeout(resolve, 100));
|
|
|
|
if (connectionCount >= maxConnections) {
|
|
return NextResponse.json(
|
|
{
|
|
success: false,
|
|
error: '資料庫連線池已滿,請稍後再試',
|
|
retryAfter: 5 // 建議5秒後重試
|
|
},
|
|
{ status: 503 }
|
|
);
|
|
}
|
|
}
|
|
|
|
connectionCount++;
|
|
|
|
try {
|
|
const result = await handler(...args);
|
|
return result;
|
|
} catch (error) {
|
|
console.error('資料庫操作錯誤:', error);
|
|
|
|
// 根據錯誤類型返回適當的響應
|
|
if (error instanceof Error) {
|
|
if (error.message.includes('ER_CON_COUNT_ERROR')) {
|
|
return NextResponse.json(
|
|
{
|
|
success: false,
|
|
error: '資料庫連線數已達上限,請稍後再試',
|
|
retryAfter: 10
|
|
},
|
|
{ status: 503 }
|
|
);
|
|
} else if (error.message.includes('ECONNRESET') || error.message.includes('PROTOCOL_CONNECTION_LOST')) {
|
|
return NextResponse.json(
|
|
{
|
|
success: false,
|
|
error: '資料庫連線中斷,請重試',
|
|
retryAfter: 3
|
|
},
|
|
{ status: 503 }
|
|
);
|
|
}
|
|
}
|
|
|
|
return NextResponse.json(
|
|
{
|
|
success: false,
|
|
error: '資料庫操作失敗',
|
|
details: error instanceof Error ? error.message : '未知錯誤'
|
|
},
|
|
{ status: 500 }
|
|
);
|
|
} finally {
|
|
connectionCount--;
|
|
}
|
|
};
|
|
}
|
|
|
|
// 連線池健康檢查
|
|
export async function checkConnectionHealth(): Promise<{
|
|
isHealthy: boolean;
|
|
connectionCount: number;
|
|
maxConnections: number;
|
|
usagePercentage: number;
|
|
}> {
|
|
try {
|
|
// 使用連線監控器獲取詳細統計
|
|
const stats = await connectionMonitor.getConnectionStats();
|
|
|
|
return {
|
|
isHealthy: stats.isHealthy,
|
|
connectionCount: stats.activeConnections,
|
|
maxConnections: stats.maxConnections,
|
|
usagePercentage: stats.usagePercentage
|
|
};
|
|
} catch (error) {
|
|
console.error('連線健康檢查失敗:', error);
|
|
return {
|
|
isHealthy: false,
|
|
connectionCount: 0,
|
|
maxConnections: 0,
|
|
usagePercentage: 100
|
|
};
|
|
}
|
|
}
|
|
|
|
// 自動釋放空閒連線
|
|
export async function autoReleaseIdleConnections(): Promise<boolean> {
|
|
try {
|
|
const health = await checkConnectionHealth();
|
|
|
|
if (health.usagePercentage > 70) {
|
|
console.log('🔄 連線使用率過高,嘗試釋放空閒連線...');
|
|
return await dbMonitor.releaseIdleConnections();
|
|
}
|
|
|
|
return true;
|
|
} catch (error) {
|
|
console.error('自動釋放連線失敗:', error);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// 連線池監控
|
|
export function startConnectionMonitoring() {
|
|
// 每30秒檢查一次連線狀態
|
|
dbMonitor.startMonitoring(30000);
|
|
|
|
// 每5分鐘嘗試釋放空閒連線
|
|
setInterval(async () => {
|
|
await autoReleaseIdleConnections();
|
|
}, 5 * 60 * 1000);
|
|
|
|
console.log('🔍 資料庫連線監控已啟動');
|
|
}
|
|
|
|
// 優雅的關閉(使用新的關閉管理器)
|
|
export async function gracefulShutdown() {
|
|
console.log('🔄 使用資料庫關閉管理器進行優雅關閉...');
|
|
await dbShutdownManager.gracefulShutdown();
|
|
}
|
|
|
|
// 強制關閉
|
|
export function forceShutdown() {
|
|
console.log('🚨 強制關閉資料庫連線...');
|
|
dbShutdownManager.forceShutdown();
|
|
}
|
|
|
|
// 獲取關閉狀態
|
|
export function getShutdownStatus() {
|
|
return dbShutdownManager.getShutdownStatus();
|
|
}
|
|
|
|
// 測試關閉機制
|
|
export async function testShutdown() {
|
|
return await dbShutdownManager.testShutdown();
|
|
}
|