|
1 | 1 | import type { RedisOptions } from "@internal/redis"; |
| 2 | +import type { PrismaClient, Waitpoint } from "@trigger.dev/database"; |
| 3 | +import { WaitpointStoreCoordinator } from "../../waitpointCoordinator/storeCoordinator.js"; |
| 4 | +import { toPrismaWaitpoint } from "../../waitpointCoordinator/waitpointShape.js"; |
2 | 5 | import { generateRunOpsId, parseWaitpointId, RunId } from "@trigger.dev/core/v3/isomorphic"; |
3 | 6 | import { RunEngine } from "../../index.js"; |
4 | 7 | import type { RunEngineOptions } from "../../types.js"; |
@@ -59,3 +62,78 @@ export function assertStoreResident(waitpointId: string): void { |
59 | 62 | ); |
60 | 63 | } |
61 | 64 | } |
| 65 | + |
| 66 | +type ArmRead = { arm: WaitpointArm; prisma: PrismaClient; redisOptions: RedisOptions }; |
| 67 | + |
| 68 | +/** |
| 69 | + * Read a waitpoint from whichever system holds it. |
| 70 | + * |
| 71 | + * A test that reads `prisma.waitpoint` directly is asserting against Postgres, and the |
| 72 | + * store path writes no row there for RUN, BATCH or DATETIME. Going through here lets one |
| 73 | + * expectation hold on both arms. |
| 74 | + */ |
| 75 | +export async function readWaitpointForArm( |
| 76 | + args: ArmRead & { waitpointId: string } |
| 77 | +): Promise<Waitpoint | null> { |
| 78 | + if (parseWaitpointId(args.waitpointId).format === "legacy") { |
| 79 | + return args.prisma.waitpoint.findFirst({ where: { id: args.waitpointId } }); |
| 80 | + } |
| 81 | + |
| 82 | + const store = new WaitpointStoreCoordinator({ redisOptions: args.redisOptions }); |
| 83 | + try { |
| 84 | + const held = await store.readWaitpoint(args.waitpointId); |
| 85 | + return held ? toPrismaWaitpoint(held.record, held.status, held.completion) : null; |
| 86 | + } finally { |
| 87 | + await store.quit(); |
| 88 | + } |
| 89 | +} |
| 90 | + |
| 91 | +export type ArmBlockEdge = { |
| 92 | + waitpointId: string; |
| 93 | + batchIndex: number | null; |
| 94 | + waitpoint: Waitpoint; |
| 95 | +}; |
| 96 | + |
| 97 | +/** |
| 98 | + * A run's blocking edges, from both systems. |
| 99 | + * |
| 100 | + * Always unions the two rather than switching on the arm, because a run can hold one edge |
| 101 | + * in each at the same time and a test that saw only half would report the wrong count. |
| 102 | + */ |
| 103 | +export async function readRunBlockEdgesForArm( |
| 104 | + args: ArmRead & { runId: string } |
| 105 | +): Promise<ArmBlockEdge[]> { |
| 106 | + const legacy = await args.prisma.taskRunWaitpoint.findMany({ |
| 107 | + where: { taskRunId: args.runId }, |
| 108 | + include: { waitpoint: true }, |
| 109 | + }); |
| 110 | + |
| 111 | + const edges: ArmBlockEdge[] = legacy.map((edge) => ({ |
| 112 | + waitpointId: edge.waitpointId, |
| 113 | + batchIndex: edge.batchIndex, |
| 114 | + waitpoint: edge.waitpoint, |
| 115 | + })); |
| 116 | + |
| 117 | + if (args.arm !== "store") { |
| 118 | + return edges; |
| 119 | + } |
| 120 | + |
| 121 | + const store = new WaitpointStoreCoordinator({ redisOptions: args.redisOptions }); |
| 122 | + try { |
| 123 | + const state = await store.readBlockState(args.runId); |
| 124 | + for (const edge of state.edges) { |
| 125 | + const held = await store.readWaitpoint(edge.waitpointId); |
| 126 | + if (held) { |
| 127 | + edges.push({ |
| 128 | + waitpointId: edge.waitpointId, |
| 129 | + batchIndex: edge.batchIndex ?? null, |
| 130 | + waitpoint: toPrismaWaitpoint(held.record, held.status, held.completion), |
| 131 | + }); |
| 132 | + } |
| 133 | + } |
| 134 | + } finally { |
| 135 | + await store.quit(); |
| 136 | + } |
| 137 | + |
| 138 | + return edges; |
| 139 | +} |
0 commit comments