Skip to content

Commit 310c37b

Browse files
committed
fix(webapp): raw gate replacement keeps the inline gate, V4 deploys validate first
The low-level gates option now carries the task's anonymous inline limit gate over the same way the concurrency option does (a replay resending stored gates collapses back through dedupe). The V4 deploy path rejects invalid concurrency declarations before any worker rows or engine keys are written, so a failed deploy can no longer leave the running version's limits mutated.
1 parent af7cac1 commit 310c37b

3 files changed

Lines changed: 40 additions & 3 deletions

File tree

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

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -248,12 +248,31 @@ export class DefaultQueueManager implements QueueManager {
248248
]
249249
: undefined;
250250

251+
/**
252+
* The raw gates option replaces stored gates the same way concurrency does, so
253+
* it also carries the inline gate over; a replay resending the stored gates
254+
* collapses back to the original set through the dedupe below.
255+
*/
256+
const rawGates = request.body.options?.gates;
251257
const requestedGates =
252-
concurrencyGates ?? request.body.options?.gates ?? taskGates ?? undefined;
258+
concurrencyGates ??
259+
(rawGates ? [...inlineTaskGates, ...rawGates] : undefined) ??
260+
taskGates ??
261+
undefined;
262+
263+
const seenGates = new Set<string>();
253264
const gates = requestedGates
254265
?.flatMap((gate) => {
255266
const sanitized = sanitizeQueueName(gate.queue);
256-
return sanitized ? [{ queue: sanitized, concurrencyKey: gate.concurrencyKey }] : [];
267+
if (!sanitized) {
268+
return [];
269+
}
270+
const dedupeKey = `${sanitized}${gate.concurrencyKey ?? ""}`;
271+
if (seenGates.has(dedupeKey)) {
272+
return [];
273+
}
274+
seenGates.add(dedupeKey);
275+
return [{ queue: sanitized, concurrencyKey: gate.concurrencyKey }];
257276
})
258277
.slice(0, 3);
259278

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -614,7 +614,7 @@ async function createWorkerQueues(
614614
* Rejects invalid concurrency declarations before any worker rows are written, so a
615615
* failed deploy leaves nothing behind for a same-content retry to return.
616616
*/
617-
function validateWorkerConcurrencyDeclarations(metadata: BackgroundWorkerMetadata): void {
617+
export function validateWorkerConcurrencyDeclarations(metadata: BackgroundWorkerMetadata): void {
618618
for (const queue of metadata.queues ?? []) {
619619
assertNotReservedQueueName(queue.name, `Queue "${queue.name}"`);
620620
}

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

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import {
1515
createWorkerResources,
1616
syncDeclarativeSchedules,
1717
syncDeclarativeWebhooks,
18+
validateWorkerConcurrencyDeclarations,
1819
} from "./createBackgroundWorker.server";
1920
import { findOrCreateBackgroundWorker } from "./createDeploymentBackgroundWorkerV4/findOrCreateBackgroundWorker.server";
2021
import { TimeoutDeploymentService } from "./timeoutDeployment.server";
@@ -67,6 +68,23 @@ export class CreateDeploymentBackgroundWorkerServiceV4 extends BaseService {
6768
return;
6869
}
6970

71+
/**
72+
* Reject invalid concurrency declarations before any worker rows exist. Queue
73+
* rows and their engine limit keys are per-environment, so a mid-creation
74+
* failure would leave the running version's limits already mutated.
75+
*/
76+
try {
77+
validateWorkerConcurrencyDeclarations(body.metadata);
78+
} catch (concurrencyError) {
79+
if (concurrencyError instanceof ServiceValidationError) {
80+
logger.warn("Invalid worker concurrency declarations", {
81+
error: concurrencyError.message,
82+
});
83+
await this.#failBackgroundWorkerDeployment(deployment, concurrencyError, environment);
84+
}
85+
throw concurrencyError;
86+
}
87+
7088
// Handle multi-platform builds
7189
const deploymentPlatforms = deployment.imagePlatform?.split(",") ?? [];
7290
if (deploymentPlatforms.length > 1) {

0 commit comments

Comments
 (0)