Skip to content

Commit 43d0722

Browse files
committed
test(run-engine): load every run-queue script into redis
Some ck slots open Lua blocks that the shared text below them closes. That is what lets the two builds share the text, and it also means a change to the shared part can leave one build's render unbalanced while the other still parses. Typecheck cannot see it, because the script is a string, and the first sign would otherwise be an EVAL failing after someone turned the flag on. Redis can see it. This registers both builds and SCRIPT LOADs everything they define, so a syntax error surfaces in CI. Verified by adding a stray end to the dequeue's shared text: two scripts fail to compile and the test reports both.
1 parent c8872e2 commit 43d0722

1 file changed

Lines changed: 82 additions & 0 deletions

File tree

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
import { redisTest } from "@internal/testcontainers";
2+
import { trace } from "@internal/tracing";
3+
import { Logger } from "@trigger.dev/core/logger";
4+
import { FairQueueSelectionStrategy } from "../fairQueueSelectionStrategy.js";
5+
import { RunQueue } from "../index.js";
6+
import { RunQueueFullKeyProducer } from "../keyProducer.js";
7+
8+
// A draining variant parks its tag in :ckVtimeIdle before it leaves :ckVtime, so its next
9+
// enqueue re-registers with the credit it earned instead of at the floor. This pins that
10+
// on every route out: ack, dead-letter and TTL expiry parking it, and nack restoring it.
11+
//
12+
// Each test gives the variant credit first. Parking a tag that equals the floor proves
13+
// nothing, because the dequeue reaps everything at or below the floor on its next call.
14+
15+
const testOptions = {
16+
name: "rq",
17+
tracer: trace.getTracer("rq"),
18+
workers: 1,
19+
defaultEnvConcurrency: 25,
20+
logger: new Logger("RunQueue", "error"),
21+
retryOptions: {
22+
maxAttempts: 5,
23+
factor: 1.1,
24+
minTimeoutInMs: 100,
25+
maxTimeoutInMs: 1_000,
26+
randomize: true,
27+
},
28+
keys: new RunQueueFullKeyProducer(),
29+
};
30+
31+
function createQueue(
32+
redisContainer: any,
33+
opts: { vtime?: boolean; maxAttempts?: number } = {}
34+
): any {
35+
const redis = {
36+
keyPrefix: "runqueue:test:",
37+
host: redisContainer.getHost(),
38+
port: redisContainer.getPort(),
39+
};
40+
return new RunQueue({
41+
...testOptions,
42+
masterQueueConsumersDisabled: true,
43+
workerOptions: { disabled: true },
44+
...(opts.maxAttempts
45+
? { retryOptions: { ...testOptions.retryOptions, maxAttempts: opts.maxAttempts } }
46+
: {}),
47+
...(opts.vtime === false ? {} : { ckVirtualTimeScheduling: { enabled: true } }),
48+
queueSelectionStrategy: new FairQueueSelectionStrategy({ redis, keys: testOptions.keys }),
49+
redis,
50+
} as any) as any;
51+
}
52+
53+
describe("run queue Lua", () => {
54+
redisTest("every defined script compiles, in both builds", async ({ redisContainer }) => {
55+
const on = createQueue(redisContainer);
56+
const off = createQueue(redisContainer, { vtime: false });
57+
try {
58+
const scripts = new Map<string, string>();
59+
for (const q of [on, off]) {
60+
for (const [name, def] of Object.entries(
61+
(q.redis as any).scriptsSet as Record<string, any>
62+
)) {
63+
scripts.set(name, (def as any).lua);
64+
}
65+
}
66+
// Both builds are registered on the same client, so this covers the vtime commands and
67+
// the flag-off commands they share their text with.
68+
expect(scripts.size).toBeGreaterThan(30);
69+
70+
const failures: string[] = [];
71+
for (const [name, lua] of scripts) {
72+
await (on.redis as any).script("LOAD", lua).catch((e: Error) => {
73+
failures.push(`${name}: ${e.message}`);
74+
});
75+
}
76+
expect(failures).toEqual([]);
77+
} finally {
78+
await off.quit();
79+
await on.quit();
80+
}
81+
});
82+
});

0 commit comments

Comments
 (0)