UNPKG

@liberation-data/drivine

Version:

Best and fastest graph database client for TypeScript / Node.js. Provides a level of abstraction for building highly scalable applications, without compromising architectural integrity

117 lines 4.78 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Transaction = void 0; const ts_data_stack_1 = require("ts-data.stack"); const assert = require("assert"); const DrivineError_1 = require("../DrivineError"); const logger_1 = require("../logger"); const short_unique_id_1 = require("short-unique-id"); const async_mutex_1 = require("async-mutex"); const shortId = new short_unique_id_1.default({ length: 7 }); class Transaction { constructor(options, contextHolder) { this.contextHolder = contextHolder; this.logger = new logger_1.DrivineLogger(Transaction.name); this.connectionMutex = new async_mutex_1.Mutex(); this.id = shortId.rnd(); this.callStack = new ts_data_stack_1.default(); this.connectionRegistry = new Map(); this.cursors = []; this.options = options; this.contextHolder.currentTransaction = this; } get description() { return `${this.id} [${this.databases}]`; } get databases() { return Array.from(this.connectionRegistry.keys()); } get connections() { return Array.from(this.connectionRegistry.values()); } async query(spec, database) { assert(this.callStack.count(), `pushContext() must be called running a query`); try { const connection = await this.connectionFor(database); const results = await connection.query(spec); return results; } catch (e) { throw DrivineError_1.DrivineError.withRootCause(e, spec); } } async openCursor(spec, database) { assert(this.callStack.count(), `pushContext() must be called running a query`); const connection = await this.connectionFor(database); const cursor = await connection.openCursor(spec); this.cursors.push(cursor); return cursor; } async pushContext(context) { if (this.callStack.isEmpty()) { this.logger.verbose(`Starting transaction: ${this.id}`); } this.callStack.push(String(context)); return Promise.resolve(); } async popContext(isRoot) { this.callStack.pop(); if (isRoot) { this.logger.verbose(`Closing ${this.cursors.length} open cursors.`); await Promise.all(this.cursors.map(async (it) => it.close())); if (this.options.rollback) { this.logger.verbose(`Transaction: ${this.description} successful, but is marked ROLLBACK. Rolling back.`); await Promise.all(this.connections.map(async (it) => it.rollbackTransaction())); } else { this.logger.verbose(`Committing transaction: ${this.description}.`); await Promise.all(this.connections.map(async (it) => it.commitTransaction())); } await this.releaseClient(); } } async popContextWithError(e, isRoot) { this.callStack.pop(); if (isRoot) { this.logger.verbose(`Rolling back transaction: ${this.description} due to error: ${e.message}.`); await Promise.all(this.connections.map(async (it) => it.rollbackTransaction())); await this.releaseClient(e); } } markAsRollback() { this._options = { ...this._options, rollback: true }; } get options() { return this._options; } set options(options) { assert(this.callStack.isEmpty(), `Can't set options if the transaction is already in flight`); this._options = options; } async connectionFor(database) { const release = await this.connectionMutex.acquire(); try { if (!this.connectionRegistry.get(database)) { const databaseRegistry = this.contextHolder.databaseRegistry; const connectionProvider = databaseRegistry.connectionProvider(database); if (!connectionProvider) { throw new DrivineError_1.DrivineError(`There is no database registered with key: ${database}`); } const connection = await connectionProvider.connect(); this.connectionRegistry.set(database, connection); await connection.startTransaction(); } return this.connectionRegistry.get(database); } finally { release(); } } async releaseClient(error) { this.logger.verbose(`Releasing connection(s) for transaction: ${this.id}`); await Promise.all(this.connections.map(async (it) => it.release(error))); this.contextHolder.currentTransaction = undefined; } } exports.Transaction = Transaction; //# sourceMappingURL=Transaction.js.map