UNPKG

@aradox/multi-orm

Version:

Type-safe ORM with multi-datasource support, row-level security, and Prisma-like API for PostgreSQL, SQL Server, and HTTP APIs

142 lines 5.32 kB
"use strict"; /** * Transaction Manager * * Provides transaction support for SQL adapters with ACID guarantees. * Supports isolation levels, timeout control, and automatic rollback on errors. */ Object.defineProperty(exports, "__esModule", { value: true }); exports.TransactionManager = exports.MSSQLTransaction = void 0; const logger_1 = require("../utils/logger"); /** * MSSQL Transaction Implementation */ class MSSQLTransaction { options; executeQuery; id; connection; isActive = true; isCommitted = false; isRolledBack = false; constructor(connection, options = {}, executeQuery) { this.options = options; this.executeQuery = executeQuery; this.connection = connection; this.id = `txn_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; } async begin() { const isolationLevel = this.options.isolationLevel || 'READ COMMITTED'; const sql = `SET TRANSACTION ISOLATION LEVEL ${isolationLevel}; BEGIN TRANSACTION`; logger_1.logger.debug('runtime', `Beginning transaction ${this.id} with isolation level: ${isolationLevel}`); await this.executeQuery(sql); } async commit() { this.ensureActive(); logger_1.logger.debug('runtime', `Committing transaction ${this.id}`); await this.executeQuery('COMMIT TRANSACTION'); this.isActive = false; this.isCommitted = true; } async rollback() { if (!this.isActive) { logger_1.logger.warn('runtime', `Transaction ${this.id} is not active, skipping rollback`); return; } logger_1.logger.debug('runtime', `Rolling back transaction ${this.id}`); try { await this.executeQuery('ROLLBACK TRANSACTION'); } catch (error) { logger_1.logger.error('runtime', `Error rolling back transaction ${this.id}: ${error.message}`); } finally { this.isActive = false; this.isRolledBack = true; } } // Forward operations to adapter but within transaction context async findMany(model, args) { this.ensureActive(); throw new Error('Transaction operations must be implemented by specific adapter'); } async findUnique(model, args) { this.ensureActive(); throw new Error('Transaction operations must be implemented by specific adapter'); } async create(model, args) { this.ensureActive(); throw new Error('Transaction operations must be implemented by specific adapter'); } async update(model, args) { this.ensureActive(); throw new Error('Transaction operations must be implemented by specific adapter'); } async delete(model, args) { this.ensureActive(); throw new Error('Transaction operations must be implemented by specific adapter'); } async count(model, args) { this.ensureActive(); throw new Error('Transaction operations must be implemented by specific adapter'); } ensureActive() { if (!this.isActive) { if (this.isCommitted) { throw new Error(`Transaction ${this.id} has already been committed`); } if (this.isRolledBack) { throw new Error(`Transaction ${this.id} has already been rolled back`); } throw new Error(`Transaction ${this.id} is not active`); } } } exports.MSSQLTransaction = MSSQLTransaction; /** * Transaction Manager for ORM Client */ class TransactionManager { /** * Execute a function within a transaction with automatic rollback on error */ static async run(beginTransaction, callback) { const tx = await beginTransaction(); try { const result = await callback(tx); await tx.commit(); return result; } catch (error) { await tx.rollback(); throw error; } } /** * Execute multiple operations in a transaction with retry logic */ static async runWithRetry(beginTransaction, callback, options = {}) { const maxRetries = options.maxRetries ?? 3; const retryDelay = options.retryDelay ?? 1000; const retryableErrors = options.retryableErrors ?? ['deadlock', 'timeout', 'connection']; let lastError = null; for (let attempt = 0; attempt <= maxRetries; attempt++) { try { return await this.run(beginTransaction, callback); } catch (error) { lastError = error; // Check if error is retryable const isRetryable = retryableErrors.some(keyword => lastError.message.toLowerCase().includes(keyword)); if (!isRetryable || attempt === maxRetries) { throw lastError; } logger_1.logger.warn('runtime', `Transaction failed (attempt ${attempt + 1}/${maxRetries + 1}), retrying in ${retryDelay}ms: ${lastError.message}`); await new Promise(resolve => setTimeout(resolve, retryDelay * (attempt + 1))); } } throw lastError; } } exports.TransactionManager = TransactionManager; //# sourceMappingURL=transaction.js.map