chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,155 @@
|
||||
import { type BatchTaskRunStatus, type Prisma } from "@trigger.dev/database";
|
||||
import { type PrismaClientOrTransaction, type PrismaReplicaClient } from "~/db.server";
|
||||
import { findDisplayableEnvironment } from "~/models/runtimeEnvironment.server";
|
||||
import { engine } from "~/v3/runEngine.server";
|
||||
import { readThroughRun } from "~/v3/runOpsMigration/readThrough.server";
|
||||
import { BasePresenter } from "./basePresenter.server";
|
||||
|
||||
type BatchPresenterOptions = {
|
||||
environmentId: string;
|
||||
batchId: string;
|
||||
userId?: string;
|
||||
};
|
||||
|
||||
// Shared by the read-through closures and the passthrough so every store path returns
|
||||
// a byte-identical row shape.
|
||||
const BATCH_SELECT = {
|
||||
id: true,
|
||||
friendlyId: true,
|
||||
status: true,
|
||||
runCount: true,
|
||||
batchVersion: true,
|
||||
createdAt: true,
|
||||
updatedAt: true,
|
||||
completedAt: true,
|
||||
processingStartedAt: true,
|
||||
processingCompletedAt: true,
|
||||
successfulRunCount: true,
|
||||
failedRunCount: true,
|
||||
idempotencyKey: true,
|
||||
errors: {
|
||||
select: {
|
||||
id: true,
|
||||
index: true,
|
||||
taskIdentifier: true,
|
||||
error: true,
|
||||
errorCode: true,
|
||||
createdAt: true,
|
||||
},
|
||||
orderBy: {
|
||||
index: "asc",
|
||||
},
|
||||
},
|
||||
} satisfies Prisma.BatchTaskRunSelect;
|
||||
|
||||
type BatchRow = Prisma.BatchTaskRunGetPayload<{ select: typeof BATCH_SELECT }>;
|
||||
|
||||
type BatchPresenterDeps = {
|
||||
/** Resolved boot constant; never awaited per-request when supplied. */
|
||||
splitEnabled?: boolean;
|
||||
newClient?: PrismaReplicaClient;
|
||||
legacyReplica?: PrismaReplicaClient;
|
||||
readThrough?: typeof readThroughRun;
|
||||
resolveDisplayableEnvironment?: typeof findDisplayableEnvironment;
|
||||
};
|
||||
|
||||
export type BatchPresenterData = Awaited<ReturnType<BatchPresenter["call"]>>;
|
||||
|
||||
export class BatchPresenter extends BasePresenter {
|
||||
constructor(
|
||||
_prisma?: PrismaClientOrTransaction,
|
||||
_replica?: PrismaClientOrTransaction,
|
||||
private readonly deps: BatchPresenterDeps = {}
|
||||
) {
|
||||
super(_prisma, _replica);
|
||||
}
|
||||
|
||||
public async call({ environmentId, batchId, userId }: BatchPresenterOptions) {
|
||||
// Reads the BatchTaskRun (run-ops) via the read-through layer: split on -> new run-ops
|
||||
// first, then the LEGACY RUN-OPS READ REPLICA only for not-yet-migrated batches (never the
|
||||
// legacy primary); split off (single-DB / self-host) -> one plain batchTaskRun.findFirst
|
||||
// (passthrough). The runtimeEnvironment (control-plane) is resolved separately because its
|
||||
// FK is physically dropped on cloud, so a batch row on the new run-ops DB cannot single-SQL
|
||||
// join to control-plane RuntimeEnvironment.
|
||||
const where = { runtimeEnvironmentId: environmentId, friendlyId: batchId } as const;
|
||||
const readBatch = (client: PrismaReplicaClient): Promise<BatchRow | null> =>
|
||||
client.batchTaskRun.findFirst({ select: BATCH_SELECT, where });
|
||||
|
||||
const readThrough = this.deps.readThrough ?? readThroughRun;
|
||||
const batchResult = await readThrough<BatchRow>({
|
||||
// The read-through key; here it is the batch friendlyId. A cuid-shaped batch friendlyId
|
||||
// classifies as LEGACY and the read-through probes both stores (new first, then legacy
|
||||
// replica); a run-ops-shaped one (cut-over orgs) classifies as NEW and reads only the new
|
||||
// store — either way the row is found on the DB that owns it.
|
||||
runId: batchId,
|
||||
environmentId,
|
||||
readNew: readBatch,
|
||||
readLegacy: readBatch,
|
||||
deps: {
|
||||
splitEnabled: this.deps.splitEnabled,
|
||||
newClient: this.deps.newClient,
|
||||
legacyReplica: this.deps.legacyReplica,
|
||||
},
|
||||
});
|
||||
|
||||
const batch =
|
||||
batchResult.source === "new" || batchResult.source === "legacy-replica"
|
||||
? batchResult.value
|
||||
: null; // not-found / past-retention => normal not-found surface
|
||||
|
||||
if (!batch) {
|
||||
throw new Error("Batch not found");
|
||||
}
|
||||
|
||||
const hasFinished = batch.status !== "PENDING" && batch.status !== "PROCESSING";
|
||||
const isV2 = batch.batchVersion === "runengine:v2";
|
||||
|
||||
// For v2 batches in PROCESSING state, get live progress from Redis
|
||||
// This provides real-time updates without waiting for the batch to complete
|
||||
let liveSuccessCount = batch.successfulRunCount ?? 0;
|
||||
let liveFailureCount = batch.failedRunCount ?? 0;
|
||||
|
||||
if (isV2 && batch.status === "PROCESSING") {
|
||||
const liveProgress = await engine.getBatchQueueProgress(batch.id);
|
||||
if (liveProgress) {
|
||||
liveSuccessCount = liveProgress.successCount;
|
||||
liveFailureCount = liveProgress.failureCount;
|
||||
}
|
||||
}
|
||||
|
||||
// Control-plane env resolved separately from the run-ops batch row (cross-seam FK dropped).
|
||||
const resolveEnv = this.deps.resolveDisplayableEnvironment ?? findDisplayableEnvironment;
|
||||
|
||||
return {
|
||||
id: batch.id,
|
||||
friendlyId: batch.friendlyId,
|
||||
status: batch.status as BatchTaskRunStatus,
|
||||
runCount: batch.runCount,
|
||||
batchVersion: batch.batchVersion,
|
||||
isV2,
|
||||
createdAt: batch.createdAt.toISOString(),
|
||||
updatedAt: batch.updatedAt.toISOString(),
|
||||
completedAt: batch.completedAt?.toISOString(),
|
||||
processingStartedAt: batch.processingStartedAt?.toISOString(),
|
||||
processingCompletedAt: batch.processingCompletedAt?.toISOString(),
|
||||
finishedAt: batch.completedAt
|
||||
? batch.completedAt.toISOString()
|
||||
: hasFinished
|
||||
? batch.updatedAt.toISOString()
|
||||
: undefined,
|
||||
hasFinished,
|
||||
successfulRunCount: liveSuccessCount,
|
||||
failedRunCount: liveFailureCount,
|
||||
idempotencyKey: batch.idempotencyKey,
|
||||
environment: await resolveEnv(environmentId, userId),
|
||||
errors: batch.errors.map((error) => ({
|
||||
id: error.id,
|
||||
index: error.index,
|
||||
taskIdentifier: error.taskIdentifier,
|
||||
error: error.error,
|
||||
errorCode: error.errorCode,
|
||||
createdAt: error.createdAt.toISOString(),
|
||||
})),
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user