@@ -312,3 +312,86 @@ describe("cross-DB write atomicity (startAttempt + createExecutionSnapshot)", ()
312312 }
313313 ) ;
314314} ) ;
315+
316+ // A run's blocking edges may straddle both DBs mid-drain, so clearBlockingWaitpoints routes the
317+ // taskRunId-keyed delete through the both-stores fan-out. The #new leg can't join a control-plane
318+ // tx, but the #legacy leg CAN — so the caller's tx (e.g. attemptFailed) must still be honored for
319+ // the legacy edges, keeping them atomic with the caller's operation instead of auto-committing.
320+ async function seedLegacyBlockingEdge (
321+ prisma14 : PrismaClient ,
322+ env : { project : { id : string } ; environment : { id : string } } ,
323+ runId : string ,
324+ suffix : string
325+ ) : Promise < void > {
326+ const waitpoint = await prisma14 . waitpoint . create ( {
327+ data : {
328+ friendlyId : `wp_${ suffix } ` ,
329+ type : "MANUAL" ,
330+ status : "PENDING" ,
331+ idempotencyKey : `idem_${ suffix } ` ,
332+ userProvidedIdempotencyKey : false ,
333+ projectId : env . project . id ,
334+ environmentId : env . environment . id ,
335+ } ,
336+ } ) ;
337+ await prisma14 . taskRunWaitpoint . create ( {
338+ data : { taskRunId : runId , waitpointId : waitpoint . id , projectId : env . project . id } ,
339+ } ) ;
340+ }
341+
342+ describe ( "fan-out deleteManyTaskRunWaitpoints honors the caller's tx on the #legacy leg" , ( ) => {
343+ heteroRunOpsPostgresTest (
344+ "rolls the #legacy edge delete back when the caller's control-plane tx rolls back" ,
345+ async ( { prisma14, prisma17 } ) => {
346+ const { router } = makeSplitRouter ( prisma14 , prisma17 ) ;
347+ const env = await seedEnvironment ( prisma14 , "legacy" , "del_tx_rb" ) ;
348+ const runId = `run_${ CUID_25 } ` ;
349+ await router . createRun (
350+ buildCreateRunInput ( {
351+ runId,
352+ friendlyId : "run_del_tx_rb" ,
353+ organizationId : env . organization . id ,
354+ projectId : env . project . id ,
355+ runtimeEnvironmentId : env . environment . id ,
356+ } )
357+ ) ;
358+ await seedLegacyBlockingEdge ( prisma14 , env , runId , "del_tx_rb" ) ;
359+
360+ await expect (
361+ prisma14 . $transaction ( async ( tx ) => {
362+ await router . deleteManyTaskRunWaitpoints ( { where : { taskRunId : runId } } , tx ) ;
363+ throw new Error ( "rollback" ) ;
364+ } )
365+ ) . rejects . toThrow ( "rollback" ) ;
366+
367+ const remaining = await prisma14 . taskRunWaitpoint . count ( { where : { taskRunId : runId } } ) ;
368+ expect ( remaining ) . toBe ( 1 ) ;
369+ }
370+ ) ;
371+
372+ heteroRunOpsPostgresTest (
373+ "still deletes the #legacy edge when the caller's tx commits" ,
374+ async ( { prisma14, prisma17 } ) => {
375+ const { router } = makeSplitRouter ( prisma14 , prisma17 ) ;
376+ const env = await seedEnvironment ( prisma14 , "legacy" , "del_tx_commit" ) ;
377+ const runId = `run_${ CUID_25 } ` ;
378+ await router . createRun (
379+ buildCreateRunInput ( {
380+ runId,
381+ friendlyId : "run_del_tx_commit" ,
382+ organizationId : env . organization . id ,
383+ projectId : env . project . id ,
384+ runtimeEnvironmentId : env . environment . id ,
385+ } )
386+ ) ;
387+ await seedLegacyBlockingEdge ( prisma14 , env , runId , "del_tx_commit" ) ;
388+
389+ await prisma14 . $transaction ( async ( tx ) => {
390+ await router . deleteManyTaskRunWaitpoints ( { where : { taskRunId : runId } } , tx ) ;
391+ } ) ;
392+
393+ const remaining = await prisma14 . taskRunWaitpoint . count ( { where : { taskRunId : runId } } ) ;
394+ expect ( remaining ) . toBe ( 0 ) ;
395+ }
396+ ) ;
397+ } ) ;
0 commit comments