/** * createDbCluster —— 多连接装配 + 降级 + destroy 去重。 * * 一期单连接够用:replica / analytics 未注入时降级到 primary(**同一引用**,可用 * `===` 断言),调用方读写分离 / 分析分流的写法不变,二期换真副本零改动。 * 本文件只做装配与降级,不碰任何具体表,不读 env。 */ import { createDb } from './create-db.js' import { DbConfigError } from './errors.js' /** * @typedef {Object} DbCluster * @property {import('knex').Knex} primary 主库(读写) * @property {import('knex').Knex} replica 只读副本(一期 === primary) * @property {import('knex').Knex} analytics 分析库(一期 === primary) * @property {() => Promise} destroy 关闭所有去重后的底层连接 */ /** * 构造多连接集群。一期 replica/analytics 可省略,省略则降级到 primary(同一引用)。 * @param {Object} cfg * @param {{url:string,pool?:Object,client?:'mysql2'}} cfg.primary 主库(读写) * @param {{url:string,pool?:Object,client?:'mysql2'}} [cfg.replica] 只读副本 * @param {{url:string,pool?:Object,client?:'mysql2'}} [cfg.analytics] 分析库 * @returns {DbCluster} * @throws {DbConfigError} primary 缺失时立即抛 */ export function createDbCluster(cfg) { if (!cfg || typeof cfg !== 'object' || !cfg.primary) { throw new DbConfigError('createDbCluster: cfg.primary is required') } const primary = createDb(cfg.primary) // 降级:未注入副本/分析库 → 显式指向 primary 同一引用(不新建第二个连同库的句柄, // 避免连接数翻倍)。二期注入真副本/分析库时各自独立。 const replica = cfg.replica ? createDb(cfg.replica) : primary const analytics = cfg.analytics ? createDb(cfg.analytics) : primary /** * 关闭所有去重后的底层连接。同一引用只 destroy 一次,绝不 double-close。 * @returns {Promise} */ async function destroy() { const seen = new Set() const tasks = [] for (const handle of [primary, replica, analytics]) { if (seen.has(handle)) continue seen.add(handle) tasks.push(handle.destroy()) } await Promise.all(tasks) } return { primary, replica, analytics, destroy } }