Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ const _pointerKeys: Exact<keyof CompletedWaitpointsPointer, "cycleSeq" | "count"

const _argsKeys: Exact<
keyof ResolveCompletedWaitpointsArgs,
"runId" | "batchId" | "pointer" | "order" | "records"
"runId" | "batchId" | "pointer" | "order" | "distinctIds" | "records"
> = true;
import { enhanceExecutionSnapshotWithWaitpoints } from "./executionSnapshotSystem.js";

Expand Down Expand Up @@ -193,6 +193,7 @@ async function assertParity(
batchId: batchId ?? undefined,
pointer: { cycleSeq: 1, count: order.length },
order,
distinctIds: [...new Set(waitpoints.map((w) => w.id))],
records: waitpoints.map(toRecord),
};
// count-carried-forward behaviour (order.length, not the record count) is covered by
Expand Down Expand Up @@ -406,6 +407,7 @@ describe("the completed-waitpoints freeze", () => {
batchId: undefined,
pointer: { cycleSeq: 1, count: 1 },
order: ["wp_hook"],
distinctIds: ["wp_hook"],
records: [toRecord(w)],
});
expect(resolved).toHaveLength(1);
Expand Down Expand Up @@ -611,6 +613,7 @@ describe("the exhaustive parity grid", () => {
batchId: readingBatchId ?? undefined,
pointer: { cycleSeq: 1, count: order.length },
order,
distinctIds: [w.id],
records: [toRecord(w)],
};
const resolved = await referenceResolver(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import type {
TaskRun,
TaskRunExecutionStatus,
} from "@trigger.dev/database";
import type { RunStore } from "@internal/run-store";
import type { CompletedWaitpointRecord, RunStore } from "@internal/run-store";
import { parseNaturalLanguageDuration } from "@trigger.dev/core/v3/isomorphic";
import type { MinimalAuthenticatedEnvironment } from "../../shared/index.js";
import { QUEUED_SNAPSHOT_DESCRIPTION, QUEUED_SNAPSHOT_STATUS } from "../consts.js";
Expand Down Expand Up @@ -34,6 +34,7 @@ export class EnqueueSystem {
batchId,
checkpointId,
completedWaitpoints,
completedWaitpointRecords,
workerId,
runnerId,
skipRunLock,
Expand All @@ -57,6 +58,7 @@ export class EnqueueSystem {
id: string;
index?: number;
}[];
completedWaitpointRecords?: CompletedWaitpointRecord[];
workerId?: string;
runnerId?: string;
skipRunLock?: boolean;
Expand Down Expand Up @@ -108,6 +110,7 @@ export class EnqueueSystem {
organizationId: env.organization.id,
checkpointId,
completedWaitpoints,
completedWaitpointRecords,
workerId,
runnerId,
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import type {
TaskRunStatus,
Waitpoint,
} from "@trigger.dev/database";
import type { RunStore } from "@internal/run-store";
import type { CompletedWaitpointRecord, RunStore } from "@internal/run-store";
import { ExecutionSnapshotNotFoundError, ServiceValidationError } from "../errors.js";
import type { HeartbeatTimeouts } from "../types.js";
import type { SystemResources } from "./systems.js";
Expand Down Expand Up @@ -173,7 +173,7 @@ async function getSnapshotWaitpointIdsWithPresence(
* This is necessary because waitpoints can have large outputs (100KB+),
* and fetching many at once can exceed Node.js string limits.
*/
async function fetchWaitpointsInChunks(
export async function fetchWaitpointsInChunks(
prisma: PrismaClientOrTransaction,
waitpointIds: string[],
runStore?: RunStore,
Expand Down Expand Up @@ -449,6 +449,7 @@ export class ExecutionSnapshotSystem {
workerId,
runnerId,
completedWaitpoints,
completedWaitpointRecords,
error,
}: {
run: { id: string; status: TaskRunStatus; attemptNumber?: number | null };
Expand All @@ -470,6 +471,7 @@ export class ExecutionSnapshotSystem {
id: string;
index?: number;
}[];
completedWaitpointRecords?: CompletedWaitpointRecord[];
error?: string;
},
// When set (inside runStore.runInTransaction), the snapshot write goes through the owning store
Expand All @@ -492,6 +494,7 @@ export class ExecutionSnapshotSystem {
workerId,
runnerId,
completedWaitpoints,
completedWaitpointRecords,
error,
},
prisma
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { timeoutError } from "@trigger.dev/core/v3";
import { parseWaitpointId } from "@trigger.dev/core/v3/isomorphic";
import type { ShardKey } from "@trigger.dev/core/v3/isomorphic";
import type { CompletedWaitpointRecord } from "@internal/run-store";
import type {
PrismaClientOrTransaction,
TaskRun,
Expand All @@ -11,7 +13,8 @@ import { assertNever } from "assert-never";
import { sendNotificationToWorker } from "../eventBus.js";
import { isFinalRunStatus } from "../statuses.js";
import { LegacyPostgresWaitpointCoordinator } from "../waitpointCoordinator/legacyPostgresCoordinator.js";
import type { WaitpointCoordinator } from "../waitpointCoordinator/types.js";
import { buildCompletedWaitpointRecords } from "../waitpointCoordinator/completedWaitpointRecords.js";
import type { RunBlockEdge, WaitpointCoordinator } from "../waitpointCoordinator/types.js";
import type { EnqueueSystem } from "./enqueueSystem.js";
import type { ExecutionSnapshotSystem } from "./executionSnapshotSystem.js";
import { getLatestExecutionSnapshot } from "./executionSnapshotSystem.js";
Expand Down Expand Up @@ -605,6 +608,13 @@ export class WaitpointSystem {
};
}
case "EXECUTING_WITH_WAITPOINTS": {
// Built inside the branch, not before the switch: the statuses above return without
// appending, and they must not pay an envelope read to do it.
const completedWaitpointRecords = await this.#completedWaitpointRecordsFor(
runId,
blockingWaitpoints
);
Comment thread
coderabbitai[bot] marked this conversation as resolved.

const newSnapshot = await this.executionSnapshotSystem.createExecutionSnapshot(
this.$.prisma,
{
Expand All @@ -627,6 +637,7 @@ export class WaitpointSystem {
id: b.waitpoint.id,
index: b.batchIndex ?? undefined,
})),
...(completedWaitpointRecords && { completedWaitpointRecords }),
}
);

Expand Down Expand Up @@ -672,6 +683,11 @@ export class WaitpointSystem {
);
}

const completedWaitpointRecords = await this.#completedWaitpointRecordsFor(
runId,
blockingWaitpoints
);

//put it back in the queue, with the original timestamp (w/ priority)
//this prioritizes dequeuing waiting runs over new runs
const newSnapshot = await this.enqueueSystem.enqueueRun({
Expand All @@ -686,6 +702,7 @@ export class WaitpointSystem {
id: b.waitpoint.id,
index: b.batchIndex ?? undefined,
})),
...(completedWaitpointRecords && { completedWaitpointRecords }),
checkpointId: snapshot.checkpointId ?? undefined,
});

Expand Down Expand Up @@ -738,6 +755,42 @@ export class WaitpointSystem {
});
}

/**
* The record set for one resume, or undefined when no blocking waitpoint carries a store-format
* id.
*
* Gated on id FORMAT, not residency. The two are not the same during a migration: a
* store-format id can still be served by the Postgres arm, exactly as run-ops ids were for
* runs. Whichever arm owns it answers, so the gate only decides whether to ask at all.
*
* That gate is what keeps this inert. `parseWaitpointId` reports legacy for every id minted
* today, so no live resume reads an envelope or writes a record until a waitpoint mints in
* store format.
*/
async #completedWaitpointRecordsFor(
runId: string,
blockingWaitpoints: RunBlockEdge[]
): Promise<CompletedWaitpointRecord[] | undefined> {
const storeFormatIds = [
...new Set(
blockingWaitpoints
.map((b) => b.waitpoint.id)
.filter((id) => parseWaitpointId(id).format === "b32hexW")
),
];

if (storeFormatIds.length === 0) {
return undefined;
}

const sources = await this.coordinator.readCompletionEnvelopes({
runId,
waitpointIds: storeFormatIds,
});

return buildCompletedWaitpointRecords(sources);
Comment thread
d-cs marked this conversation as resolved.
}

/**
* Builds the waitpoint output payload from a completed run's stored output/error.
*/
Expand Down
Loading