Skip to content

Commit 19c2759

Browse files
committed
fix(run-store): forward the caller transaction to the legacy store in createExecutionSnapshot
RoutingRunStore.createExecutionSnapshot accepted a caller transaction but never forwarded it to the routed store. Forward it when the owning store is legacy so a legacy-resident snapshot stays atomic with the caller's operation; a new (cross-DB) write still cannot join a control-plane transaction, so it is dropped there and relies on runInTransaction for atomicity.
1 parent 0a2ed96 commit 19c2759

2 files changed

Lines changed: 66 additions & 1 deletion

File tree

internal-packages/run-store/src/PostgresRunStore.writeAtomicity.test.ts

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -395,3 +395,65 @@ describe("fan-out deleteManyTaskRunWaitpoints honors the caller's tx on the #leg
395395
}
396396
);
397397
});
398+
399+
// RoutingRunStore.createExecutionSnapshot accepts a caller tx but must forward it to the OWNING store
400+
// only when that store is #legacy: a control-plane tx can't wrap a #new (cross-DB) write, but it can
401+
// (and should) wrap a legacy-resident snapshot so it stays atomic with the caller's operation.
402+
describe("createExecutionSnapshot honors the caller's tx on the #legacy owning store", () => {
403+
heteroRunOpsPostgresTest(
404+
"rolls the snapshot back when a legacy run's caller tx rolls back",
405+
async ({ prisma14, prisma17 }) => {
406+
const { router } = makeSplitRouter(prisma14, prisma17);
407+
const env = await seedEnvironment(prisma14, "legacy", "ces_rb");
408+
const runId = `run_${CUID_25}`;
409+
await router.createRun(
410+
buildCreateRunInput({
411+
runId,
412+
friendlyId: "run_ces_rb",
413+
organizationId: env.organization.id,
414+
projectId: env.project.id,
415+
runtimeEnvironmentId: env.environment.id,
416+
})
417+
);
418+
419+
await expect(
420+
prisma14.$transaction(async (tx) => {
421+
await router.createExecutionSnapshot(snapshotInput(runId, env), tx);
422+
throw new Error("rollback");
423+
})
424+
).rejects.toThrow("rollback");
425+
426+
const snap = await prisma14.taskRunExecutionSnapshot.findFirst({
427+
where: { runId, executionStatus: "EXECUTING" },
428+
});
429+
expect(snap).toBeNull();
430+
}
431+
);
432+
433+
heteroRunOpsPostgresTest(
434+
"persists the snapshot when the legacy caller tx commits",
435+
async ({ prisma14, prisma17 }) => {
436+
const { router } = makeSplitRouter(prisma14, prisma17);
437+
const env = await seedEnvironment(prisma14, "legacy", "ces_commit");
438+
const runId = `run_${CUID_25}`;
439+
await router.createRun(
440+
buildCreateRunInput({
441+
runId,
442+
friendlyId: "run_ces_commit",
443+
organizationId: env.organization.id,
444+
projectId: env.project.id,
445+
runtimeEnvironmentId: env.environment.id,
446+
})
447+
);
448+
449+
await prisma14.$transaction(async (tx) => {
450+
await router.createExecutionSnapshot(snapshotInput(runId, env), tx);
451+
});
452+
453+
const snap = await prisma14.taskRunExecutionSnapshot.findFirst({
454+
where: { runId, executionStatus: "EXECUTING" },
455+
});
456+
expect(snap).not.toBeNull();
457+
}
458+
);
459+
});

internal-packages/run-store/src/runOpsStore.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -827,7 +827,10 @@ export class RoutingRunStore implements RunStore {
827827
input: CreateExecutionSnapshotInput,
828828
tx?: PrismaClientOrTransaction
829829
): Promise<Prisma.TaskRunExecutionSnapshotGetPayload<{ include: { checkpoint: true } }>> {
830-
return (await this.#routeOrNewForWrite(input.run.id)).createExecutionSnapshot(input);
830+
// Forward the caller's control-plane tx only to the #legacy store; a #new (cross-DB) write can't
831+
// join it, so it's dropped there (the atomic #new path uses runInTransaction instead).
832+
const store = await this.#routeOrNewForWrite(input.run.id);
833+
return store.createExecutionSnapshot(input, store === this.#legacy ? tx : undefined);
831834
}
832835

833836
// Snapshot ids are cuids (they always classify LEGACY), and a snapshot's CompletedWaitpoint join

0 commit comments

Comments
 (0)