UNPKG

kysely-replication

Version:
226 lines (222 loc) 7.44 kB
"use strict"; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/index.ts var index_exports = {}; __export(index_exports, { KyselyReplicationDialect: () => KyselyReplicationDialect, KyselyReplicationDriver: () => KyselyReplicationDriver }); module.exports = __toCommonJS(index_exports); // src/connection.ts var import_kysely = require("kysely"); var PRIMARY_OPERATION_NODE_KINDS = { AlterTableNode: true, CreateIndexNode: true, CreateSchemaNode: true, CreateTableNode: true, CreateTypeNode: true, CreateViewNode: true, DeleteQueryNode: true, DropIndexNode: true, DropSchemaNode: true, DropTableNode: true, DropTypeNode: true, DropViewNode: true, InsertQueryNode: true, MergeQueryNode: true, RawNode: true, UpdateQueryNode: true }; var KyselyReplicationConnection = class { #primaryDriver; #getReplicaDriver; #onReplicaTransaction; #connection; #driver; constructor(primary, getReplica, onReplicaTransaction) { this.#primaryDriver = primary; this.#getReplicaDriver = getReplica; this.#onReplicaTransaction = onReplicaTransaction; this.#connection = null; this.#driver = null; } async executeQuery(compiledQuery) { const { connection } = await this.#acquireDriverAndConnection(compiledQuery); return await connection.executeQuery(compiledQuery); } async *streamQuery(compiledQuery, chunkSize) { const { connection } = await this.#acquireDriverAndConnection(compiledQuery); for await (const result of connection.streamQuery( compiledQuery, chunkSize )) { yield result; } } async beginTransaction(settings) { const { connection, driver } = await this.#acquireDriverAndConnection("transaction"); if (driver !== this.#primaryDriver) { const message = "KyselyReplication: transaction started with replica connection!"; if (this.#onReplicaTransaction === "error") { throw new Error(message); } if (this.#onReplicaTransaction === "warn") { console.warn(message); } } await driver.beginTransaction(connection, settings); } async commitTransaction() { if (!this.#connection) { throw new Error("commitTransaction called without a transaction"); } await this.#driver?.commitTransaction(this.#connection); } async rollbackTransaction() { if (!this.#connection) { throw new Error("rollbackTransaction called without a transaction"); } await this.#driver?.rollbackTransaction(this.#connection); } async release() { if (!this.#connection) return; await this.#driver?.releaseConnection(this.#connection); } async #acquireDriverAndConnection(compiledQueryOrContext) { if (this.#connection && this.#driver) { return { connection: this.#connection, driver: this.#driver }; } this.#driver = compiledQueryOrContext === "transaction" || this.#isQueryForPrimary(compiledQueryOrContext) ? this.#primaryDriver : await this.#getReplicaDriver(compiledQueryOrContext); this.#connection = await this.#driver.acquireConnection(); return { connection: this.#connection, driver: this.#driver }; } #isQueryForPrimary(compiledQuery) { const { query } = compiledQuery; if ("__dialect__" in query) { return query.__dialect__ === "primary"; } return this.#isOperationNodeForPrimary(query) || import_kysely.SelectQueryNode.is(query) && Boolean( query.with?.expressions.some( (e) => this.#isOperationNodeForPrimary(e.expression) ) ); } #isOperationNodeForPrimary(node) { return PRIMARY_OPERATION_NODE_KINDS[node.kind]; } }; // src/driver.ts var KyselyReplicationDriver = class { #primaryDriver; #replicaDrivers; #replicaStrategy; constructor(primaryDriver, replicaDrivers, replicaStrategy) { this.#primaryDriver = primaryDriver; this.#replicaDrivers = replicaDrivers; this.#replicaStrategy = replicaStrategy; } async acquireConnection() { return new KyselyReplicationConnection( this.#primaryDriver, async (compiledQuery) => { const replicaIndex = "__replicaIndex__" in compiledQuery.query ? compiledQuery.query.__replicaIndex__ : await this.#replicaStrategy.next(this.#replicaDrivers.length); const replicaDriver = this.#replicaDrivers[replicaIndex]; if (!replicaDriver) { throw new Error( `KyselyReplication: no replicas found at index ${replicaIndex}!` ); } return replicaDriver; }, this.#replicaStrategy.onTransaction || "error" ); } async beginTransaction(connection, settings) { await connection.beginTransaction(settings); } async commitTransaction(connection) { await connection.commitTransaction(); } async destroy() { const results = await Promise.allSettled([ this.#primaryDriver.destroy(), ...this.#replicaDrivers.map((replica) => replica.destroy()) ]); const errors = this.#compileErrors(results); if (errors.length) { throw new AggregateError( errors, "KyselyReplicationDriver.destroy failed!" ); } } async init() { const results = await Promise.allSettled([ this.#primaryDriver.init(), ...this.#replicaDrivers.map((replica) => replica.init()) ]); const errors = this.#compileErrors(results); if (errors.length) { throw new AggregateError(errors, "KyselyReplicationDriver.init failed!"); } } async releaseConnection(connection) { await connection.release(); } async rollbackTransaction(connection) { await connection.rollbackTransaction(); } #compileErrors(results) { return results.map( (result, index) => result.status === "fulfilled" ? null : `${!index ? "primary" : `replica-${index - 1}`}: ${result.reason}` ).filter(Boolean); } }; // src/dialect.ts var KyselyReplicationDialect = class { #config; constructor(config) { this.#config = { ...config, replicaDialects: [...config.replicaDialects] }; } createAdapter() { return this.#config.primaryDialect.createAdapter(); } createDriver() { return new KyselyReplicationDriver( this.#config.primaryDialect.createDriver(), this.#config.replicaDialects.map((replica) => replica.createDriver()), this.#config.replicaStrategy ); } createIntrospector(db) { return this.#config.primaryDialect.createIntrospector(db); } createQueryCompiler() { return this.#config.primaryDialect.createQueryCompiler(); } }; // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { KyselyReplicationDialect, KyselyReplicationDriver }); //# sourceMappingURL=index.cjs.map