import { randomUUID } from 'node:crypto'; import { Prisma } from '../../../generated/prisma'; import { PrismaService } from '../../prisma/prisma.service'; import { assertLivingColonies, LivingColony } from './assert-living-colonies'; import { BulkCreateResult } from './bulk-create'; import { BULK_TRANSACTION_TIMEOUT_MS } from './bulk-limits'; /** * Writes the record of one entry - record, audit entry and domain event, * all through `tx`. `colony` is the entry's colony as read under the share * lock; `bulkId` goes into the event payload. */ export type BulkEntryWork = ( tx: Prisma.TransactionClient, entry: E, colony: LivingColony, bulkId: string, ) => Promise; /** * The frame of every bulk entry, all or none: one transaction with * BULK_TRANSACTION_TIMEOUT_MS, first assertLivingColonies for all colonies, * then `perEntry` once per entry in request order. The first failure rolls * back every record; the records come back in request order with a new * bulkId. */ export async function runBulk( prisma: PrismaService, entries: readonly E[], perEntry: BulkEntryWork, ): Promise> { const bulkId = randomUUID(); const records = await prisma.$transaction( async (tx) => { const colonies = await assertLivingColonies( tx, entries.map((entry) => entry.colonyId), ); const created: T[] = []; for (const entry of entries) { // Every requested id is in the map - assertLivingColonies threw // otherwise. const colony = colonies.get(entry.colonyId)!; created.push(await perEntry(tx, entry, colony, bulkId)); } return created; }, { timeout: BULK_TRANSACTION_TIMEOUT_MS }, ); return { bulkId, records }; }