fix(pipeline): keep an urgent export urgent through a validation override
Deploy / deploy (push) Successful in 6m11s
Deploy / deploy (push) Successful in 6m11s
Overriding a failed validation queued the preview at priority 50 regardless. It took its rank from the export's last PREVIEW_ONLY job — but validation runs before the preview stage, so a VALIDATION_FAILED export has never had one, and the missing row fell through to a hardcoded normal priority. An urgent shot lost its urgency at exactly the moment someone was standing over it waiting. Every preview now derives its rank from the export's AE_RENDER priority rather than from a previous preview. That fixes the missing-row case and also removes a slow drift: a preview's priority is already boosted, so re-deriving from it moved an export a step up the queue on every retry. createPreviewJob takes the render priority and the attempt number, so the first build, a retry, a regenerate and an override all rank identically. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -193,13 +193,40 @@ export function previewPriority(renderPriority: number, boost: number, urgentThr
|
||||
return Math.max(0, boosted);
|
||||
}
|
||||
|
||||
/** Creates the PREVIEW_ONLY job that follows a successful EXR render. */
|
||||
/**
|
||||
* The priority an export's work runs at: the one its render was queued with.
|
||||
*
|
||||
* Every preview for an export derives from this rather than from a previous
|
||||
* preview job, for two reasons. A preview job may not exist yet — an export
|
||||
* that failed validation never had one, and reading a missing row used to
|
||||
* silently drop an urgent export to normal. And deriving from an already-
|
||||
* boosted preview would boost again on each retry, drifting the export up the
|
||||
* queue a step at a time.
|
||||
*/
|
||||
export async function renderPriorityFor(
|
||||
tx: Prisma.TransactionClient,
|
||||
exportId: string
|
||||
): Promise<number> {
|
||||
const job = await tx.renderJob.findFirst({
|
||||
where: { exportId, type: "AE_RENDER" },
|
||||
orderBy: { attempt: "desc" },
|
||||
select: { priority: true },
|
||||
});
|
||||
return job?.priority ?? 50;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a PREVIEW_ONLY job. `renderPriority` is the export's *render*
|
||||
* priority; the queue rank is derived from it here so every path — first
|
||||
* build, retry, regenerate, validation override — ranks identically.
|
||||
*/
|
||||
export async function createPreviewJob(
|
||||
tx: Prisma.TransactionClient,
|
||||
exportRow: ExportForPreview,
|
||||
manifest: PreviewManifest,
|
||||
maxAttempts: number,
|
||||
priority: number
|
||||
renderPriority: number,
|
||||
attempt = 1
|
||||
) {
|
||||
const [boost, urgentThreshold] = await Promise.all([
|
||||
getConfigNumber("render.previewPriorityBoost"),
|
||||
@@ -210,9 +237,9 @@ export async function createPreviewJob(
|
||||
data: {
|
||||
type: "PREVIEW_ONLY",
|
||||
exportId: exportRow.id,
|
||||
attempt: 1,
|
||||
attempt,
|
||||
maxAttempts,
|
||||
priority: previewPriority(priority, boost, urgentThreshold),
|
||||
priority: previewPriority(renderPriority, boost, urgentThreshold),
|
||||
manifest: manifest as unknown as Prisma.InputJsonValue,
|
||||
},
|
||||
});
|
||||
@@ -421,18 +448,17 @@ export async function retryPreview(exportId: string, actorId?: string) {
|
||||
const last = await tx.renderJob.findFirst({
|
||||
where: { exportId, type: "PREVIEW_ONLY" },
|
||||
orderBy: { attempt: "desc" },
|
||||
select: { attempt: true },
|
||||
});
|
||||
|
||||
const job = await tx.renderJob.create({
|
||||
data: {
|
||||
type: "PREVIEW_ONLY",
|
||||
exportId,
|
||||
attempt: (last?.attempt ?? 0) + 1,
|
||||
maxAttempts,
|
||||
priority: last?.priority ?? 50,
|
||||
manifest: manifest as unknown as Prisma.InputJsonValue,
|
||||
},
|
||||
});
|
||||
const job = await createPreviewJob(
|
||||
tx,
|
||||
exportRow,
|
||||
manifest,
|
||||
maxAttempts,
|
||||
await renderPriorityFor(tx, exportId),
|
||||
(last?.attempt ?? 0) + 1
|
||||
);
|
||||
|
||||
const updated = await applyTransition(tx, exportRow, "GENERATING_PREVIEW", {
|
||||
type: "USER",
|
||||
@@ -501,18 +527,17 @@ export async function regeneratePreview(exportId: string, input: RegeneratePrevi
|
||||
const last = await tx.renderJob.findFirst({
|
||||
where: { exportId, type: "PREVIEW_ONLY" },
|
||||
orderBy: { attempt: "desc" },
|
||||
select: { attempt: true },
|
||||
});
|
||||
|
||||
const job = await tx.renderJob.create({
|
||||
data: {
|
||||
type: "PREVIEW_ONLY",
|
||||
exportId,
|
||||
attempt: (last?.attempt ?? 0) + 1,
|
||||
maxAttempts,
|
||||
priority: last?.priority ?? 50,
|
||||
manifest: manifest as unknown as Prisma.InputJsonValue,
|
||||
},
|
||||
});
|
||||
const job = await createPreviewJob(
|
||||
tx,
|
||||
refreshed,
|
||||
manifest,
|
||||
maxAttempts,
|
||||
await renderPriorityFor(tx, exportId),
|
||||
(last?.attempt ?? 0) + 1
|
||||
);
|
||||
|
||||
const updated = await applyTransition(
|
||||
tx,
|
||||
|
||||
@@ -4,7 +4,7 @@ import { db } from "@/lib/db";
|
||||
import { PipelineError } from "./errors";
|
||||
import { applyTransition } from "./transitions";
|
||||
import { getConfigBoolean, getConfigNumber, getConfigValue, getPreviewConfig } from "./config";
|
||||
import { buildPreviewManifest, createPreviewJob, inheritPreviewMedia } from "./preview";
|
||||
import { buildPreviewManifest, createPreviewJob, inheritPreviewMedia, renderPriorityFor } from "./preview";
|
||||
|
||||
/**
|
||||
* Validation engine results (RenderPipeline2 §8). The worker runs the checks —
|
||||
@@ -301,17 +301,20 @@ export async function overrideValidation(
|
||||
const last = await tx.renderJob.findFirst({
|
||||
where: { exportId, type: "PREVIEW_ONLY" },
|
||||
orderBy: { attempt: "desc" },
|
||||
select: { attempt: true },
|
||||
});
|
||||
const previewJob = await tx.renderJob.create({
|
||||
data: {
|
||||
type: "PREVIEW_ONLY",
|
||||
exportId,
|
||||
attempt: (last?.attempt ?? 0) + 1,
|
||||
maxAttempts,
|
||||
priority: last?.priority ?? 50,
|
||||
manifest: manifest as unknown as Prisma.InputJsonValue,
|
||||
},
|
||||
});
|
||||
// An export that failed validation has never had a preview job — validation
|
||||
// runs before the preview stage — so the rank comes from its render, which
|
||||
// is what keeps an urgent export urgent through an override. Someone is
|
||||
// usually standing over an urgent shot when they wave a check through.
|
||||
const previewJob = await createPreviewJob(
|
||||
tx,
|
||||
exportRow,
|
||||
manifest,
|
||||
maxAttempts,
|
||||
await renderPriorityFor(tx, exportId),
|
||||
(last?.attempt ?? 0) + 1
|
||||
);
|
||||
|
||||
const updated = await applyTransition(tx, exportRow, "GENERATING_PREVIEW", {
|
||||
type: "USER",
|
||||
|
||||
Reference in New Issue
Block a user