/** * Transactional Metadata Service * * Provides atomic operations on metadata with row-level locking (FOR UPDATE). * Critical for financial operations, inventory, counters, or any scenario * where concurrent updates could cause race conditions. * * Key features: * - SELECT FOR UPDATE row-level locking * - Automatic deadlock retry with exponential backoff * - Lock timeout protection * - RLS-aware (uses app.user_id) * - Synchronous update functions to avoid holding locks during I/O * * Usage example: * ```typescript * const newBalance = await TransactionalMetaService.updateWithLock( * 'user', * userId, * 'credits', * (current: CreditBalance) => ({ * ...current, * balance: current.balance - cost * }), * userId * ) * ``` */ import { EntityType } from '../../types/meta.types'; /** * Options for transactional operations */ export interface TransactionalOptions { /** * Lock timeout in milliseconds (default: 5000ms) * Prevents indefinite waiting for locks */ lockTimeout?: number; /** * Retry on deadlock (default: true) * Automatically retries when PostgreSQL detects deadlock (error code 40P01) */ retryOnDeadlock?: boolean; /** * Maximum retry attempts (default: 3) */ maxRetries?: number; /** * Initial retry delay in milliseconds (default: 100ms) * Uses exponential backoff: delay * (2 ^ attempt) */ initialRetryDelay?: number; } /** * Error thrown when a transactional operation fails */ export declare class TransactionalError extends Error { code?: string; originalError?: Error; constructor(message: string, code?: string, originalError?: Error); } /** * Service for atomic metadata operations with row-level locking */ export declare class TransactionalMetaService { private static readonly DEFAULT_OPTIONS; /** * Update metadata with row-level lock (SELECT FOR UPDATE) * * CRITICAL: updateFn MUST be synchronous to avoid holding locks during I/O operations * * @param entityType - Entity type (e.g., 'user', 'product') * @param entityId - Entity ID * @param metaKey - Metadata key * @param updateFn - Synchronous function to transform current value * @param userId - User ID for RLS context * @param options - Transaction options * @returns Updated value * * @throws TransactionalError if update fails * @throws Error if updateFn is async (holds lock during I/O) */ static updateWithLock(entityType: EntityType, entityId: string, metaKey: string, updateFn: (current: T | null) => T, userId: string, options?: TransactionalOptions): Promise; /** * Bulk update multiple metadata keys atomically * * @param entityType - Entity type * @param entityId - Entity ID * @param updates - Object with metaKey: updateFn pairs * @param userId - User ID for RLS context * @param options - Transaction options * @returns Object with updated values */ static bulkUpdateWithLock>(entityType: EntityType, entityId: string, updates: Record unknown>, userId: string, options?: TransactionalOptions): Promise; /** * Delete metadata with lock * * @param entityType - Entity type * @param entityId - Entity ID * @param metaKey - Metadata key to delete * @param userId - User ID for RLS context * @returns true if deleted, false if not found */ static deleteWithLock(entityType: EntityType, entityId: string, metaKey: string, userId: string): Promise; } //# sourceMappingURL=transactional-meta.service.d.ts.map