Files
MetonaSqlark/src/connection-manager.ts
T
thzxx 0f128da34a
CI / test (18.x) (push) Successful in 9m54s
CI / test (20.x) (push) Successful in 9m52s
CI / test (22.x) (push) Successful in 9m54s
CI / test (24.x) (push) Successful in 9m47s
feat: v0.1.13 — 事务回滚 + 子查询 + 外键级联 + 连接池
2026-07-26 16:31:15 +08:00

132 lines
3.6 KiB
TypeScript

/**
* metona-sqlark Connection Manager — 数据库实例连接池
* @module connection-manager
*
* v0.1.13: 避免重复创建同名数据库实例,通过 connect() 复用已有连接。
* 管理实例生命周期,防止重复 open IndexedDB。
*/
import { MetonaSqlark } from './core';
import type { DatabaseConfig } from './constants';
// ---------------------------------------------------------------------------
// ConnectionManager
// ---------------------------------------------------------------------------
class ConnectionManager {
/** 活跃连接:dbName → MetonaSqlark */
private connections = new Map<string, MetonaSqlark>();
/** 连接引用计数:dbName → count */
private refCount = new Map<string, number>();
/**
* 获取或创建数据库实例
*
* 如果同名数据库已打开,复用已有实例并增加引用计数。
* 否则创建新实例。
*
* @example
* ```ts
* const db = await MetonaSqlark.connect({ name: 'my-app', mode: 'hybrid' });
* // ... use db
* await db.disconnect(); // 引用计数 -1,归零时自动关闭
* ```
*/
async connect(config: DatabaseConfig): Promise<MetonaSqlark> {
const name = config.name;
// 已有连接 → 复用
const existing = this.connections.get(name);
if (existing && existing.isReady()) {
const count = (this.refCount.get(name) ?? 0) + 1;
this.refCount.set(name, count);
return existing;
}
// 创建新连接
const db = new MetonaSqlark(config);
await db.init();
this.connections.set(name, db);
this.refCount.set(name, 1);
// 注入 disconnect 方法
(db as MetonaSqlark & { disconnect: () => Promise<void> }).disconnect = async () => {
await this.release(name);
};
return db;
}
/**
* 释放连接引用。引用计数归零时自动关闭数据库。
*/
async release(dbName: string): Promise<void> {
const count = (this.refCount.get(dbName) ?? 1) - 1;
if (count <= 0) {
const db = this.connections.get(dbName);
if (db) {
await db.close();
this.connections.delete(dbName);
}
this.refCount.delete(dbName);
} else {
this.refCount.set(dbName, count);
}
}
/**
* 强制关闭指定数据库(忽略引用计数)
*/
async forceClose(dbName: string): Promise<void> {
const db = this.connections.get(dbName);
if (db) {
await db.close();
this.connections.delete(dbName);
}
this.refCount.delete(dbName);
}
/**
* 强制关闭所有连接
*/
async closeAll(): Promise<void> {
for (const [, db] of this.connections) {
try { await db.close(); } catch { /* ignore */ }
}
this.connections.clear();
this.refCount.clear();
}
/**
* 获取所有活跃连接名
*/
getActiveConnections(): string[] {
return Array.from(this.connections.keys());
}
/**
* 获取连接的引用计数
*/
getRefCount(dbName: string): number {
return this.refCount.get(dbName) ?? 0;
}
}
// ---------------------------------------------------------------------------
// 全局单例
// ---------------------------------------------------------------------------
const manager = new ConnectionManager();
// 挂载到 MetonaSqlark 静态方法(通过 any 绕过 TS 类型检查)
const M = MetonaSqlark as unknown as Record<string, unknown>;
M.connect = (config: DatabaseConfig) => manager.connect(config);
M.disconnect = (dbName: string) => manager.release(dbName);
M.disconnectAll = () => manager.closeAll();
M.getActiveConnections = () => manager.getActiveConnections();
export { manager as connectionManager };
export default manager;