@damoxing/datasource-manager
v0.4.0
Published
HTTP-agnostic multi-datasource lifecycle, routing, heartbeat, and recovery manager.
Maintainers
Readme
@damoxing/datasource-manager 中文文档
@damoxing/datasource-manager 是一个与 HTTP 框架无关的 Node.js 多数据源生命周期管理库。它负责数据源配置加载、路由、健康状态、连接池恢复、心跳、重试、优雅停机、运行期治理和基础可观测性。
它不是 ORM,也不负责实体映射、SQL 迁移、业务接口或 Web 路由。核心目标是把多数据库连接池管理从业务项目里独立出来,形成可发布、可测试、可复用的基础库。
功能概览
- 多数据源配置加载和状态管理
- 默认数据源、严格路由、
route_keys路由 - 兼容老业务字段
jgbh_list和getByJgbh() - Oracle、达梦 DM、PostgreSQL、GaussDB/openGauss、Kingbase 适配器
- 统一连接池治理配置和驱动字段映射
- 心跳检查、连接池恢复、连接异常查询重试
- 稳定健康输出
getHealth() - 指标输出
getMetrics() - 运行期新增、删除、更新和重载数据源
- 读写分离和分组路由
- 配置密钥解析和日志脱敏
- 进程信号优雅停机
- CJS、ESM 和 TypeScript 类型声明
安装
npm install @damoxing/datasource-manager数据库驱动是可选 peer dependency,只需要安装实际使用的驱动:
npm install pg
npm install oracledb
npm install dmdbpg 同时用于 PostgreSQL、GaussDB/openGauss 和 Kingbase。
基础使用
CommonJS:
const {
DataSourceManager,
createDefaultAdapterFactories
} = require('@damoxing/datasource-manager');
const manager = new DataSourceManager({
config,
logger,
adapterFactories: createDefaultAdapterFactories({
logger,
logParams: 'redacted'
}),
shutdownSignals: ['SIGTERM', 'SIGINT']
});
await manager.initialize();
const db = manager.getByRouteKey('tenant-a');
const row = await db.get('SELECT 1 AS health_check');
await manager.closeAll();ESM:
import {
DataSourceManager,
createDefaultAdapterFactories
} from '@damoxing/datasource-manager';
const manager = new DataSourceManager({
config,
adapterFactories: createDefaultAdapterFactories()
});配置结构
{
"strict_routing": true,
"default_datasource": "pg_main",
"datasources": [
{
"id": "pg_main",
"type": "pg",
"jgbh_list": ["1001"],
"route_keys": ["tenant-a"],
"config": {
"host": "127.0.0.1",
"port": 5432,
"database": "app",
"user": "app",
"password": "env:DB_PASSWORD"
}
}
]
}字段说明:
strict_routing:开启后,未知路由会直接抛错,不回落默认库。default_datasource:没有传路由 key 时使用的默认数据源。datasources[].id:数据源唯一标识。datasources[].type:数据源类型,支持oracle、dm、pg、gauss、opengauss、kingbase。jgbh_list:兼容业务机构编号路由。route_keys:通用路由 key,推荐新代码优先使用。config:传给具体数据库驱动的连接配置。
路由使用
推荐新代码使用通用路由:
const db = manager.getByRouteKey('tenant-a');老业务可以继续使用机构编号:
const db = manager.getByJgbh('1001');默认库:
const db = manager.getDefaultHandle();直接按数据源 ID 获取:
const db = manager.getHandle('pg_main');Codex MCP Server
本包内置 stdio MCP server,Codex 可以通过同一套 DataSourceManager 和数据库适配器检查指定数据源:
npm run build
DAMOXING_MCP_CONFIG=/absolute/path/to/datasources.json node dist/cjs/mcp/server.js安装包后也可以使用 bin 入口:
damoxing-datasource-mcpMCP server 当前提供:
damoxing_health:查看脱敏健康状态和指标。damoxing_query:只读 SQL 查询。damoxing_list_tables/damoxing_describe_table:查看表、字段、索引、约束。damoxing_list_routines/damoxing_get_routine_definition:查看函数、过程、包等对象。damoxing_explain:查看只读 SQL 执行计划。damoxing_exec:显式 SQL 执行。damoxing_open_session:打开 MCP 专用固定物理连接会话。damoxing_session_query/damoxing_session_exec:在同一个sessionId和同一个物理连接上执行 SQL。damoxing_session_commit/damoxing_session_rollback:提交或回滚固定会话事务。damoxing_cancel_session_operation:在驱动支持时取消当前会话操作。damoxing_get_notices:获取并清空固定会话捕获的 NOTICE。damoxing_get_audit_events:获取经过脱敏的有界固定会话审计事件。damoxing_close_session:回滚未提交事务并释放固定连接。
同一个固定会话内的操作会串行执行,不会并发使用同一个连接。PostgreSQL 兼容数据库的固定会话会显式开启事务;关闭会话或连接异常时会先回滚再释放连接。固定连接丢失后不会自动切换连接重试,而是返回结构化的 SESSION_CONNECTION_LOST 错误。
默认只允许只读会话。读写会话必须同时设置 DAMOXING_MCP_ALLOW_WRITE=true,并在打开会话和写操作时传 confirmWrite=true。DROP、TRUNCATE、DELETE、UPDATE、ALTER 等破坏性 SQL 还需要额外设置 DAMOXING_MCP_ALLOW_DESTRUCTIVE=true 和 confirmDestructive=true。
需要由运维方把生产数据源 ID 配置到 DAMOXING_MCP_PRODUCTION_DATASOURCES;这些数据源默认拒绝调试。只有同时设置 DAMOXING_MCP_ALLOW_PRODUCTION_DEBUG=true,且打开会话时传 confirmProduction=true,才允许建立固定会话。
固定会话限制可通过以下环境变量设置:
DAMOXING_MCP_MAX_SESSIONS:全局最大会话数,默认5。DAMOXING_MCP_MAX_SESSIONS_PER_DATASOURCE:单数据源最大会话数,默认2。DAMOXING_MCP_SESSION_IDLE_TIMEOUT_MS:空闲超时,默认300000毫秒。DAMOXING_MCP_SESSION_MAX_LIFETIME_MS:绝对生命周期,默认1800000毫秒。DAMOXING_MCP_SESSION_CLEANUP_INTERVAL_MS:过期清理间隔,默认30000毫秒。DAMOXING_MCP_AUDIT_MAX_EVENTS:内存审计事件上限,默认1000。
固定会话注册表会保留有界的内存审计记录,内容包括动作、结果、数据源/会话标识、耗时、行数、SQL 类型、归一化 SQL 指纹以及参数数量/参数名。审计记录不会保存 SQL 原文和参数值,可通过 damoxing_get_audit_events 查询。
固定会话使用独立且延迟初始化的 MCP 数据源管理器。直接 import / require('@damoxing/datasource-manager') 不会加载 MCP SDK、创建 MCP 定时器或预留调试连接,因此不改变 Node.js 项目的直接使用方式。
2026 年 7 月 15 日使用本地 OrbStack 数据库完成的固定会话实测结果:
| 数据库 | 固定连接/事务 | 会话状态 | NOTICE | 超时/取消 |
| --- | --- | --- | --- | --- |
| PostgreSQL | 已通过 | 临时表、会话变量已通过 | 已通过 | statement_timeout 和显式取消已通过 |
| openGauss | 已通过 | 临时表、会话变量已通过 | 已通过 | statement_timeout 和显式取消已通过 |
| Oracle | 已通过 | SID、CLIENT_INFO 已通过 | 不支持 PostgreSQL NOTICE 语义 | callTimeout 已通过,超时后连接仍可用 |
| 达梦 DM | 已通过 | 会话 ID、全局临时表已通过 | 当前驱动不支持 | 当前 dmdb 驱动不提供可靠取消/操作超时,MCP 会明确返回不支持 |
| Kingbase | 未完成 | 本地容器因开发许可证过期无法启动数据库进程 | 未验证 | 未验证 |
可分别运行 npm run test:integration:session:pg、npm run test:integration:session:opengauss、npm run test:integration:session:oracle 和 npm run test:integration:session:dm 复验。测试只创建隔离的临时或探针对象,并在结束时清理。
当前阶段只是“固定会话调试基础”,还不是完整的存储过程运行时调试。结构化 OUT/INOUT、游标句柄与 FETCH、过程性能 Trace、断点和单步能力仍按路线图后续阶段实现。
仓库内置 Codex skill 位于 skills/damoxing-database-mcp。
连接池治理
可以在 datasource 顶层配置统一 pool 参数:
{
"id": "pg_main",
"type": "pg",
"pool": {
"minPoolSize": 1,
"maxPoolSize": 10,
"acquireTimeoutMs": 3000,
"connectTimeoutMs": 3000,
"validationTimeoutMs": 1000,
"idleTimeoutMs": 60000,
"maxLifetimeMs": 1800000,
"keepaliveMs": 30000
},
"config": {}
}也可以写在 config.pool 里。管理器会把统一字段映射到不同驱动的字段,并在不支持某个字段时通过结构化日志输出 unsupportedPoolOptions。
当前映射重点:
- Oracle:
poolMin、poolMax、queueTimeout、connectTimeout、poolTimeout、maxLifetimeSession、poolPingInterval - DM:
poolMin、poolMax - PostgreSQL/Gauss/Kingbase:
min、max、connectionTimeoutMillis、idleTimeoutMillis、maxLifetimeSeconds
如果 minPoolSize > maxPoolSize,初始化配置时会直接抛错。
也可以直接调用工具函数做诊断:
const { applyPoolGovernance } = require('@damoxing/datasource-manager');
const result = applyPoolGovernance('pg', {}, {
minPoolSize: 1,
maxPoolSize: 5
});健康状态
getHealth() 返回稳定、脱敏的健康结构:
const health = manager.getHealth();核心字段:
initializedstrictRoutingdefaultDatasourceIdrouteCountheartbeat.enabledheartbeat.runningshutdownHooks.installedsummary.totalsummary.readysummary.failedsummary.unhealthysummary.byStatusdatasources[].iddatasources[].typedatasources[].statusdatasources[].lastError
健康输出不会包含 datasource 原始配置,因此不会暴露密码和连接串。
指标输出
getMetrics() 用于生产可观测性:
const metrics = manager.getMetrics();当前包含:
- 初始化成功和失败次数
- 心跳成功和失败次数
- 恢复尝试、成功和失败次数
- 查询成功和失败次数
- 查询连接异常次数
- 查询重试次数
- 事务连接异常次数
- 初始化、心跳、恢复、查询延迟摘要
指标标签保持有限且安全,只包含 id、type、status 等字段,不包含 SQL、参数、密码或连接串。
心跳和恢复
初始化成功后,心跳会执行轻量 SQL:
- Oracle/DM:
SELECT 1 AS health_check FROM DUAL - 其他:
SELECT 1 AS health_check
当 heartbeat 发现连接异常时,会关闭旧 adapter,重新创建 pool,并再次 ping。查询过程中遇到连接类异常时,会恢复 pool 并重试一次。
事务不会自动重放。如果事务执行中出现连接异常,管理器会恢复 pool,但会把原始异常抛出,避免重复提交或业务状态不一致。
运行期数据源治理
可以在服务运行中变更数据源:
await manager.addDatasource({
id: 'tenant_a',
type: 'pg',
route_keys: ['TENANT_A'],
config: {}
});
await manager.updateDatasource('tenant_a', {
id: 'tenant_a',
type: 'pg',
route_keys: ['TENANT_A'],
config: {}
});
await manager.removeDatasource('tenant_a');
await manager.reloadConfig(nextConfig);updateDatasource() 会先初始化并 ping 新配置,确认 ready 后才关闭旧 pool。如果新配置失败,旧数据源继续保留。
reloadConfig() 会重建数据源集合,适合配置中心刷新或管理后台变更。
读写分离和分组路由
配置示例:
{
"datasources": [
{ "id": "tenant_a_master", "type": "pg", "config": {} },
{ "id": "tenant_a_read_1", "type": "pg", "config": {} },
{ "id": "tenant_a_read_2", "type": "pg", "config": {} }
],
"groups": {
"tenant_a": {
"write": "tenant_a_master",
"read": [
"tenant_a_read_1",
{ "id": "tenant_a_read_2", "weight": 2 }
],
"strategy": "round-robin",
"fallbackToWrite": true
}
}
}使用:
const readDb = manager.getReadHandle('tenant_a');
const writeDb = manager.getWriteHandle('tenant_a');支持的读路由策略:
round-robinrandomweightedfirst-ready
不健康的读库会被跳过。如果全部读库不可用,并且 fallbackToWrite !== false,读请求会回落到写库。
配置安全
支持四种常用方式:
{
"password": "env:DB_PASSWORD",
"token": "secret:database/token",
"connectString": "postgres://app:${DB_PASSWORD}@localhost/db",
"encrypted": "ENC(ciphertext)"
}初始化:
const manager = new DataSourceManager({
env: process.env,
secretResolver: async key => loadSecretFromVault(key),
decryptor: async value => decrypt(value),
config
});说明:
${DB_PASSWORD}:从env中插值。env:DB_PASSWORD:整个字段从环境变量读取。secret:database/token:通过secretResolver读取。ENC(ciphertext):通过decryptor解密。
解析出来的密钥会从健康状态、指标、结构化错误和管理器日志中脱敏。
日志
数据源异常日志会带结构化上下文:
{
datasourceId,
type,
jgbh,
jgbhList,
routeKeys,
status,
lastError,
error
}SQL 文本默认记录,SQL 参数默认不记录。可以按需开启参数脱敏日志:
const adapterFactories = createDefaultAdapterFactories({
logger,
logSql: true,
logParams: 'redacted',
redactKeys: ['password', 'token', 'secret'],
redactValue: '[REDACTED]'
});logParams 可选值:
false或'off':不记录参数。true或'redacted':记录脱敏参数。'raw':记录原始参数,只建议本地调试使用。
优雅停机
const manager = new DataSourceManager({
shutdownSignals: ['SIGTERM', 'SIGINT'],
exitOnShutdownSignal: true,
adapterFactories,
config
});收到信号后,管理器会停止 heartbeat,移除 shutdown hook,关闭全部 pool,并把数据源状态标记为 closed。
也可以手动调用:
await manager.closeAll();发布和验证
本地验证:
npm test
npm run release:checkrelease:check 会执行:
- TypeScript 构建
- CJS smoke test
- ESM smoke test
- TypeScript 类型 smoke test
npm pack --dry-run
发布到 npm 前,应确认:
package.json版本已更新。CHANGELOG.md已记录版本。npm test通过。npm run release:check通过。- npm Trusted Publisher 已配置。
- GitHub Release 使用新版本 tag。
当前边界
本库当前不做:
- ORM 实体映射
- SQL migration
- 自动重放事务
- HTTP 路由绑定
- 业务权限判断
- 分布式事务核心实现
这些能力可以通过独立 adapter 或上层应用接入,不建议塞进核心包。
后续路线
后续待办见 ROADMAP.md。当前 P0/P1 中,连接池治理、metrics、运行期数据源治理、读写分组和配置安全已经具备基础实现;Prometheus/OpenTelemetry adapter、真实数据库 idle/maxLifetime 集成测试、DM worker 隔离仍在后续迭代中。
