import { db, getConnectionStatus } from '~/drizzle/db' import { sql } from 'drizzle-orm' import { getConnectionPoolStatus, getDatabaseMetrics } from './database-health' import { getServerTimestamp } from './serverTime' /** * 数据库管理器 */ export class DatabaseManager { private static instance: DatabaseManager private healthCheckCache: { status: boolean; timestamp: number; latency: number } | null = null private readonly CACHE_TTL = 30000 // 30秒缓存 private constructor() {} static getInstance(): DatabaseManager { if (!DatabaseManager.instance) { DatabaseManager.instance = new DatabaseManager() } return DatabaseManager.instance } /** * 数据库健康检查(带缓存) */ async healthCheck(): Promise<{ status: boolean latency: number timestamp: Date connectionStatus: string error?: string }> { const now = getServerTimestamp() // 检查缓存 if (this.healthCheckCache && now - this.healthCheckCache.timestamp < this.CACHE_TTL) { return { status: this.healthCheckCache.status, latency: this.healthCheckCache.latency, timestamp: new Date(this.healthCheckCache.timestamp), connectionStatus: 'cached' } } const startTime = Date.now() try { // 获取连接状态 const connectionStatus = await getConnectionStatus() // 执行简单查询测试连接 await db.execute(sql`SELECT 1 as health_check`) const latency = Date.now() - startTime const result = { status: true, latency, timestamp: new Date(), connectionStatus: connectionStatus.status } // 更新缓存 this.healthCheckCache = { status: true, timestamp: now, latency } return result } catch (error) { const latency = Date.now() - startTime const result = { status: false, latency, timestamp: new Date(), connectionStatus: 'error', error: error instanceof Error ? error.message : 'Unknown error' } // 更新缓存 this.healthCheckCache = { status: false, timestamp: now, latency } return result } } /** * 获取基础数据库信息 - 简化版本适配 Neon Database */ async getBasicMetrics(): Promise<{ databaseSize: string activeConnections: number serverless: boolean }> { try { // 获取数据库大小 const sizeResult = await db.execute(sql` SELECT pg_size_pretty(pg_database_size(current_database())) as database_size `) // 获取当前连接数(简化版) const connectionStats = await db.execute(sql` SELECT count(*) as active_connections FROM pg_stat_activity WHERE datname = current_database() AND state = 'active' `) const sizeRow = sizeResult[0] as { database_size?: string } | undefined const connectionRow = connectionStats[0] as { active_connections?: number | string } | undefined return { databaseSize: sizeRow?.database_size || 'Unknown', activeConnections: Number.parseInt(String(connectionRow?.active_connections ?? '0'), 10) || 0, serverless: true // Neon Database 是无服务器架构 } } catch (error) { console.error('[DatabaseManager] Failed to get basic metrics:', error) throw new Error( `Failed to retrieve basic metrics: ${error instanceof Error ? error.message : 'Unknown error'}` ) } } /** * 获取连接状态 - 适配 Neon Database 无服务器架构 */ async getConnectionStatus(): Promise<{ connected: boolean status: string activeConnections: number serverlessMode: boolean autoSuspend: boolean error: string | null }> { try { const connectionStatus = await getConnectionStatus() // 用最小查询判断真实连通性,避免底层连接对象状态或统计查询误判。 await db.execute(sql`SELECT 1 as connection_check`) // 获取当前活跃连接数 let activeConnections = 0 try { const connectionStats = await db.execute(sql` SELECT count(*) as active_connections FROM pg_stat_activity WHERE datname = current_database() AND state = 'active' `) const connRow = connectionStats[0] as { active_connections?: number | string } | undefined activeConnections = Number.parseInt(String(connRow?.active_connections ?? '0'), 10) || 0 } catch (metricsError) { console.warn('Failed to get active connection count:', metricsError) } return { connected: true, status: connectionStatus.status || 'connected', activeConnections, serverlessMode: true, // Neon Database 是无服务器架构 autoSuspend: true, // 支持自动暂停 error: null } } catch (error) { console.error('Failed to get connection status:', error) return { connected: false, status: 'error', activeConnections: 0, serverlessMode: true, autoSuspend: true, error: error instanceof Error ? error.message : 'Failed to retrieve connection status' } } } /** * 获取连接池状态 */ async getConnectionPoolStatus() { return await getConnectionPoolStatus() } /** * 获取数据库性能指标 */ async getPerformanceMetrics() { return await getDatabaseMetrics() } /** * 批量清理过期会话 */ async cleanupExpiredSessions(): Promise { try { const result = await db.execute(sql` DELETE FROM session WHERE expires_at < NOW() `) // postgres-js returns count in the result array object properties const deleteResult = result as { count?: number | string } return Number.parseInt(String(deleteResult.count ?? '0'), 10) || 0 } catch (error) { console.error('Failed to cleanup expired sessions:', error) throw new Error('Failed to cleanup expired sessions') } } /** * 清除健康检查缓存 */ clearHealthCheckCache(): void { this.healthCheckCache = null } /** * 获取数据库管理器状态 */ getManagerStatus(): { cacheEnabled: boolean cacheTTL: number lastHealthCheck: Date | null } { return { cacheEnabled: true, cacheTTL: this.CACHE_TTL, lastHealthCheck: this.healthCheckCache ? new Date(this.healthCheckCache.timestamp) : null } } } // 导出单例实例 export const databaseManager = DatabaseManager.getInstance()