Skip to content

Commit e3f7ed4

Browse files
committed
fix(webapp): deploys re-assert pause, reserve limit/ in task gates, four-gate backstop
A deploy syncing a paused queue writes the paused zero limit instead of skipping, and the post-deploy convergence loop also re-syncs when the paused flag moved, so a pause or resume landing mid-deploy can no longer leave the engine enforcing the wrong limit. Client-supplied task gates may not claim the reserved limit/ namespace. The gate overflow backstop moves to four to match the engine capacity.
1 parent 74b27e8 commit e3f7ed4

2 files changed

Lines changed: 24 additions & 4 deletions

File tree

apps/webapp/app/runEngine/concerns/queues.server.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -274,9 +274,15 @@ export class DefaultQueueManager implements QueueManager {
274274
return [{ queue: sanitized, concurrencyKey: gate.concurrencyKey }];
275275
});
276276

277-
if (gates && gates.length > 3) {
277+
/**
278+
* Unreachable through the public schemas (three requested gates plus one inline
279+
* gate is the ceiling), kept as a backstop so an overflowing set can never be
280+
* silently truncated downstream. Replays of three-gate runs against a task that
281+
* later gained an inline limit resolve to four and stay valid.
282+
*/
283+
if (gates && gates.length > 4) {
278284
throw new ServiceValidationError(
279-
`A run can hold at most three gates (the task's inline limit plus two named limits); this request resolves to ${gates.length}.`
285+
`A run can hold at most four gates; this request resolves to ${gates.length}.`
280286
);
281287
}
282288

apps/webapp/app/v3/services/createBackgroundWorker.server.ts

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -628,6 +628,10 @@ export function validateWorkerConcurrencyDeclarations(metadata: BackgroundWorker
628628
assertNotReservedQueueName(task.queue.name, `Task "${task.id}"`);
629629
}
630630

631+
for (const gate of task.gates ?? []) {
632+
assertNotReservedQueueName(gate.queue, `Task "${task.id}" gate "${gate.queue}"`);
633+
}
634+
631635
const concurrency = task.concurrency;
632636
if (!concurrency) {
633637
continue;
@@ -803,13 +807,20 @@ async function createWorkerQueue(
803807
await removeQueueConcurrencyLimits(environment, row.name);
804808
}
805809
} else {
806-
logger.debug("createWorkerQueue: queue is paused, not updating concurrency limit", {
810+
/**
811+
* A paused queue's engine limit is 0 (what pause wrote). Re-asserting it here
812+
* heals the race where a pause lands between this deploy's row read and its
813+
* engine sync, which would otherwise overwrite the 0 with the declared limit
814+
* and leave a queue the dashboard shows as paused still dequeuing.
815+
*/
816+
logger.debug("createWorkerQueue: queue is paused, re-asserting the paused limit", {
807817
workerId: worker.id,
808818
taskQueue: row,
809819
orgId: environment.organizationId,
810820
projectId: environment.projectId,
811821
environmentId: environment.id,
812822
});
823+
await updateQueueConcurrencyLimits(environment, row.name, 0);
813824
}
814825
};
815826

@@ -827,6 +838,7 @@ async function createWorkerQueue(
827838
let syncedMarkers = {
828839
concurrency: taskQueue.concurrencyLimitOverriddenAt?.getTime(),
829840
total: taskQueue.totalConcurrencyLimitOverriddenAt?.getTime(),
841+
paused: taskQueue.paused,
830842
};
831843

832844
for (let i = 0; i < 3; i++) {
@@ -845,7 +857,8 @@ async function createWorkerQueue(
845857
if (
846858
!freshQueue ||
847859
(freshQueue.concurrencyLimitOverriddenAt?.getTime() === syncedMarkers.concurrency &&
848-
freshQueue.totalConcurrencyLimitOverriddenAt?.getTime() === syncedMarkers.total)
860+
freshQueue.totalConcurrencyLimitOverriddenAt?.getTime() === syncedMarkers.total &&
861+
freshQueue.paused === syncedMarkers.paused)
849862
) {
850863
break;
851864
}
@@ -854,6 +867,7 @@ async function createWorkerQueue(
854867
syncedMarkers = {
855868
concurrency: freshQueue.concurrencyLimitOverriddenAt?.getTime(),
856869
total: freshQueue.totalConcurrencyLimitOverriddenAt?.getTime(),
870+
paused: freshQueue.paused,
857871
};
858872
}
859873

0 commit comments

Comments
 (0)