Skip to content

Commit b88c7ff

Browse files
1stvampTrigger.dev RepoOps
authored andcommitted
fix(webapp): send keyless canary runs to the stable container lane
Fixed an issue where some runs from deployments without a prebuilt template could stay queued instead of starting. Mono-RevId: 2a8e5c624437ab80277ea56bb7b97ea6cb932230
1 parent c5b123d commit b88c7ff

6 files changed

Lines changed: 91 additions & 5 deletions

File tree

‎apps/webapp/app/runEngine/concerns/computeMigration.server.ts‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,8 @@ export function resolveComputeMigration({
7979
workerQueue: string | undefined;
8080
region: string | undefined;
8181
enableFastPath: boolean;
82+
/** Set when the missing template key kept a migrated run on the base queue. */
83+
templateFallback?: true;
8284
} {
8385
const passthrough = { workerQueue: baseWorkerQueue, region, enableFastPath: baseEnableFastPath };
8486
if (baseWorkerQueue === undefined) return passthrough;
@@ -91,6 +93,6 @@ export function resolveComputeMigration({
9193
? false
9294
: isOrgMigrated(decision);
9395
if (!migrated) return passthrough;
94-
if (!templateKey) return passthrough;
96+
if (!templateKey) return { ...passthrough, templateFallback: true };
9597
return { workerQueue: backing.workerQueue, region, enableFastPath: backing.enableFastPath };
9698
}

‎apps/webapp/app/runEngine/concerns/workerQueueAssignment.server.test.ts‎

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -229,11 +229,13 @@ describe("per-environment worker queue overrides", () => {
229229
environmentType = "STAGING",
230230
v2Enabled = true,
231231
migrationEnabled = false,
232+
templateKey = "a".repeat(64),
232233
}: {
233234
orgFeatureFlags: Record<string, unknown>;
234235
environmentType?: string;
235236
v2Enabled?: boolean;
236237
migrationEnabled?: boolean;
238+
templateKey?: string | null;
237239
}) {
238240
const environmentOverride = environmentWorkerQueueOverride({
239241
orgFeatureFlags,
@@ -251,7 +253,7 @@ describe("per-environment worker queue overrides", () => {
251253
flags: { computeMigrationEnabled: migrationEnabled, computeMigrationFreePercentage: 100 },
252254
envType: environmentType,
253255
environmentCompat: environmentOverride.compat,
254-
templateKey: "a".repeat(64),
256+
templateKey,
255257
});
256258
const workerQueue = workerQueueForBirth({
257259
workerQueue: migrated.workerQueue!,
@@ -263,6 +265,7 @@ describe("per-environment worker queue overrides", () => {
263265
splitEnabled: true,
264266
workerGroups,
265267
rootTriggerSource: "api",
268+
templateFallback: migrated.templateFallback,
266269
});
267270
return { workerQueue, enableFastPath: migrated.enableFastPath };
268271
}
@@ -386,5 +389,39 @@ describe("per-environment worker queue overrides", () => {
386389
enableFastPath: true,
387390
});
388391
});
392+
393+
it("puts a migrated org's keyless run on the stable container channel", () => {
394+
const canaryStaging = {
395+
[FEATURE_FLAG.workerQueueEnvironmentOverrides]: { STAGING: { channel: "canary" } },
396+
};
397+
const canaryOrg = { [FEATURE_FLAG.workerQueueChannel]: "canary" };
398+
for (const orgFeatureFlags of [canaryStaging, canaryOrg]) {
399+
expect(trigger({ orgFeatureFlags, migrationEnabled: true, templateKey: null })).toEqual({
400+
workerQueue: "us-east-1:v2:ondemand:fresh:container:stable",
401+
enableFastPath: false,
402+
});
403+
// A run that is not migrated keeps its channel, keyless or not.
404+
expect(trigger({ orgFeatureFlags, templateKey: null })).toEqual({
405+
workerQueue: "us-east-1:v2:ondemand:fresh:container:canary",
406+
enableFastPath: false,
407+
});
408+
}
409+
});
410+
411+
it("keeps an explicit compute compatibility for a keyless run, for the dequeue check", () => {
412+
expect(
413+
trigger({
414+
orgFeatureFlags: {
415+
[FEATURE_FLAG.workerQueueCompatibility]: "compute",
416+
[FEATURE_FLAG.workerQueueChannel]: "canary",
417+
},
418+
migrationEnabled: true,
419+
templateKey: null,
420+
})
421+
).toEqual({
422+
workerQueue: "us-east-1:v2:ondemand:fresh:compute:canary",
423+
enableFastPath: false,
424+
});
425+
});
389426
});
390427
});

‎apps/webapp/app/runEngine/concerns/workerQueueAssignment.server.ts‎

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ export function workerQueueForBirth({
4949
splitEnabled,
5050
workerGroups,
5151
rootTriggerSource,
52+
templateFallback,
5253
}: {
5354
workerQueue: string;
5455
region: string | undefined;
@@ -59,6 +60,8 @@ export function workerQueueForBirth({
5960
splitEnabled: boolean;
6061
workerGroups: readonly Pick<WorkerGroupRegionRow, "masterQueue" | "workloadType">[];
6162
rootTriggerSource: string | undefined;
63+
/** The run stays off compute because its deployment has no template key. */
64+
templateFallback?: boolean;
6265
}): string {
6366
if (envType === "DEVELOPMENT") {
6467
return workerQueue;
@@ -79,13 +82,16 @@ export function workerQueueForBirth({
7982
const channel = FeatureFlagCatalog[FEATURE_FLAG.workerQueueChannel].safeParse(
8083
environmentOverride.channel ?? orgFeatureFlags?.[FEATURE_FLAG.workerQueueChannel] ?? "stable"
8184
);
85+
const compat = compatibility.success ? compatibility.data : defaultCompatibility;
86+
// Match the dequeue fallback: container consumers take only the stable channel.
87+
const keylessOnContainers = templateFallback && compat === "container";
8288
const assignment = formatWorkerQueue({
8389
region: region ?? workerQueue,
8490
version: WORKER_QUEUE_VERSION,
8591
class: "ondemand",
8692
phase: "fresh",
87-
compat: compatibility.success ? compatibility.data : defaultCompatibility,
88-
channel: channel.success ? channel.data : "stable",
93+
compat,
94+
channel: !keylessOnContainers && channel.success ? channel.data : "stable",
8995
});
9096

9197
return workerQueueForRun({

‎apps/webapp/app/runEngine/services/triggerTask.server.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -563,6 +563,7 @@ export class RunEngineTriggerTaskService {
563563
splitEnabled: env.TRIGGER_WORKER_QUEUE_SCHEDULED_SPLIT_ENABLED === "1",
564564
workerGroups,
565565
rootTriggerSource: annotations.rootTriggerSource,
566+
templateFallback: migrated.templateFallback,
566567
})
567568
: migrated.workerQueue;
568569

‎apps/webapp/test/computeMigration.test.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -312,7 +312,7 @@ describe("resolveComputeMigration template key", () => {
312312
for (const templateKey of [null, undefined, ""]) {
313313
expect(
314314
resolveComputeMigration({ ...input, ...migrated, environmentCompat, templateKey })
315-
).toEqual(base);
315+
).toEqual({ ...base, templateFallback: true });
316316
}
317317
});
318318
it(`swaps a migrated org's keyed deployment to compute (compat=${environmentCompat})`, () => {

‎apps/webapp/test/engine/triggerTask.templateKey.test.ts‎

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import { containerTest } from "@internal/testcontainers";
3030
import type { PrismaClient } from "@trigger.dev/database";
3131
import { Redis } from "ioredis";
3232
import { resolveComputeMigration } from "~/runEngine/concerns/computeMigration.server";
33+
import { workerQueueForBirth } from "~/runEngine/concerns/workerQueueAssignment.server";
3334
import { DefaultQueueManager } from "~/runEngine/concerns/queues.server";
3435
import type { RunEngine } from "~/v3/runEngine.server";
3536
import { RedisTaskMetadataCache } from "~/services/taskMetadataCache.server";
@@ -211,6 +212,45 @@ describe("template key in the task metadata cache", () => {
211212
}
212213
);
213214

215+
containerTest(
216+
"a keyless promotion births a canary-channel migrated run on the stable container queue",
217+
async ({ prisma, redisOptions }) => {
218+
db.client = prisma;
219+
const environment = await setupAuthenticatedEnvironment(prisma, "PRODUCTION");
220+
const { deployment } = await createDeployment(prisma, environment, {
221+
version: "20260101.1",
222+
});
223+
const cache = newCache(redisOptions);
224+
await new ChangeCurrentDeploymentService(prisma, prisma, cache).call(deployment, "promote");
225+
226+
const queues = new DefaultQueueManager(prisma, noEngine, undefined, cache);
227+
const { templateKey } = await queues.resolveQueueProperties({
228+
taskId: SLUG,
229+
environment,
230+
body: { payload: {} },
231+
} as any);
232+
const orgFeatureFlags = { ...migratedOrg.orgFeatureFlags, workerQueueChannel: "canary" };
233+
const migrated = resolveComputeMigration({ ...migratedOrg, orgFeatureFlags, templateKey });
234+
const birth = workerQueueForBirth({
235+
workerQueue: migrated.workerQueue!,
236+
region: migrated.region,
237+
envType: "PRODUCTION",
238+
orgFeatureFlags,
239+
environmentOverride: {},
240+
v2Enabled: true,
241+
splitEnabled: false,
242+
workerGroups: [
243+
{ masterQueue: "us-east-1", workloadType: "CONTAINER" },
244+
{ masterQueue: "us-east-1-next", workloadType: "MICROVM" },
245+
],
246+
rootTriggerSource: "api",
247+
templateFallback: migrated.templateFallback,
248+
});
249+
// Container consumers take only stable, so a canary container queue would strand the run.
250+
expect(birth).toBe("us-east-1:v2:ondemand:fresh:container:stable");
251+
}
252+
);
253+
214254
containerTest(
215255
"a late build stamps its own worker's hashes but not a newer promotion's env hash",
216256
async ({ prisma, redisOptions }) => {

0 commit comments

Comments
 (0)