import { getContainer } from '@bytelyst/cosmos'; import { config } from '../config/index.js'; import logger from '../utils/logger.js'; const CONTAINER_NAME = 'runtime_locks'; type LockScope = 'entry' | 'reconciliation'; interface RuntimeLockDocument { id: string; productId: string; scope: LockScope; profileId: string; symbol?: string; owner: string; expiresAt: number; updatedAt: string; ttl: number; } function isCosmosConfigured(): boolean { return Boolean(config.COSMOS_ENDPOINT && config.COSMOS_KEY); } function buildEntryLockId(profileId: string, symbol?: string): string { return `entry:${profileId}:${String(symbol || '').trim()}`; } function buildReconciliationLockId(profileId: string): string { return `reconciliation:${profileId}`; } async function readLock(id: string): Promise { const container = getContainer(CONTAINER_NAME); try { const { resource } = await container.item(id, config.PRODUCT_ID).read(); return resource || null; } catch (error) { const code = (error as { code?: number })?.code; if (code === 404) return null; throw error; } } async function writeLock(doc: RuntimeLockDocument): Promise { const container = getContainer(CONTAINER_NAME); await container.items.upsert(doc); return true; } async function deleteLock(id: string): Promise { const container = getContainer(CONTAINER_NAME); try { await container.item(id, config.PRODUCT_ID).delete(); return true; } catch (error) { const code = (error as { code?: number })?.code; if (code === 404) return false; throw error; } } function isActive(doc: RuntimeLockDocument | null | undefined): boolean { return Boolean(doc && Number(doc.expiresAt) > Date.now()); } export async function tryAcquireEntryLock( profileId: string, symbol: string | undefined, owner: string, ttlSeconds: number ): Promise { if (!profileId || !owner) return false; if (!isCosmosConfigured()) return false; try { const id = buildEntryLockId(profileId, symbol); const existing = await readLock(id); if (isActive(existing) && existing?.owner !== owner) { return false; } await writeLock({ id, productId: config.PRODUCT_ID, scope: 'entry', profileId, symbol: String(symbol || '').trim() || undefined, owner, expiresAt: Date.now() + Math.max(1, ttlSeconds) * 1000, updatedAt: new Date().toISOString(), ttl: Math.max(1, ttlSeconds) }); return true; } catch (error) { logger.warn(`[DistributedLockRepo] Cosmos entry-lock acquire failed: ${error instanceof Error ? error.message : 'unknown error'}`); return false; } } export async function releaseEntryLock( profileId: string, symbol: string | undefined, owner: string ): Promise { if (!profileId || !owner) return false; if (!isCosmosConfigured()) return false; try { const id = buildEntryLockId(profileId, symbol); const existing = await readLock(id); if (!existing) return false; if (existing.owner !== owner) return false; return await deleteLock(id); } catch (error) { logger.warn(`[DistributedLockRepo] Cosmos entry-lock release failed: ${error instanceof Error ? error.message : 'unknown error'}`); return false; } } export async function tryAcquireReconciliationLock( profileId: string, owner: string, ttlSeconds: number ): Promise { if (!profileId || !owner) return false; if (!isCosmosConfigured()) return false; try { const id = buildReconciliationLockId(profileId); const existing = await readLock(id); if (isActive(existing) && existing?.owner !== owner) { return false; } await writeLock({ id, productId: config.PRODUCT_ID, scope: 'reconciliation', profileId, owner, expiresAt: Date.now() + Math.max(1, ttlSeconds) * 1000, updatedAt: new Date().toISOString(), ttl: Math.max(1, ttlSeconds) }); return true; } catch (error) { logger.warn(`[DistributedLockRepo] Cosmos reconciliation-lock acquire failed: ${error instanceof Error ? error.message : 'unknown error'}`); return false; } } export async function releaseReconciliationLock( profileId: string, owner: string ): Promise { if (!profileId || !owner) return false; if (!isCosmosConfigured()) return false; try { const id = buildReconciliationLockId(profileId); const existing = await readLock(id); if (!existing) return false; if (existing.owner !== owner) return false; return await deleteLock(id); } catch (error) { logger.warn(`[DistributedLockRepo] Cosmos reconciliation-lock release failed: ${error instanceof Error ? error.message : 'unknown error'}`); return false; } } export async function isEntryLockActive( profileId: string, symbol: string | undefined ): Promise { if (!profileId) return false; if (!isCosmosConfigured()) return false; try { const existing = await readLock(buildEntryLockId(profileId, symbol)); return isActive(existing); } catch (error) { logger.warn(`[DistributedLockRepo] Cosmos entry-lock status failed: ${error instanceof Error ? error.message : 'unknown error'}`); return true; } }