import { Injectable } from "@nestjs/common"; import { type Prisma } from "@prisma/client"; /** * Transaction-scoped serialization protocol for policy subjects. * * Every transaction that reads or writes block, sanction, or account-status * policy MUST call this method first for every affected account. IDs are * de-duplicated and sorted so overlapping multi-account operations cannot * deadlock. The lock is released by PostgreSQL only when the transaction ends; * callers must therefore pass the transaction client, never PrismaService. */ @Injectable() export class SafetyLockService { async lockAccounts( tx: Prisma.TransactionClient, accountIds: readonly string[], ): Promise { const canonicalIds = Array.from(new Set(accountIds)).sort(); for (const accountId of canonicalIds) { await tx.$executeRaw` SELECT pg_advisory_xact_lock( hashtextextended(${`safety:${accountId}`}, 0) ) `; } } }