@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
JavaScript
;
/**
* 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