chore(webapp,core): remove the end-of-life v3 (engine V1) execution stack (#4236)

## Summary

v3 (the engine that ran the SDK v3 era, internally
`RunEngineVersion.V1`) is end-of-life. Following the removal of the v3
execution apps
([#4194](https://github.com/triggerdotdev/trigger.dev/pull/4194)) and
the legacy dev websocket
([#4198](https://github.com/triggerdotdev/trigger.dev/pull/4198)), this
removes the remaining v3 execution stack from the server.

Clients still on v3 (an old SDK or CLI that has not upgraded) keep
getting a clear "upgrade to v4" response. Triggers, batch triggers,
reschedules, and deploys that resolve to v3 are rejected with a graceful
4xx pointing at the migration guide, never a 5xx, so a stale client
cannot affect server health. Self-hosted instances still running v3
should stay on the 4.5.x release line until they migrate.

## What is removed

- The MarQS queue and its shared/dev queue consumers.
- The v3 socket.io namespaces (coordinator, provider, shared-queue) and
the v3 run lifecycle services (attempt, checkpoint, and batch-resume).
- The graphile-worker background job system; all live jobs already run
on `@trigger.dev/redis-worker`.
- The `DEPRECATE_V3_ENABLED` flag: v3 is now rejected unconditionally,
so the flag is gone.
- Unused v3 exports from `@trigger.dev/core` (the `v3/zodNamespace`
subpath and the legacy socket message catalogs) and the now-dead MarQS
environment variables.

## What stays

The v4 engine is untouched. The graceful v3 rejection boundary stays,
`determineEngineVersion` still detects a v3 project so it can reject it,
and the batch service plus batch-completion worker stay for current
clients. Live queue concurrency limits and metrics now read from the v4
run engine instead of MarQS, and a brand-new dev environment now
defaults to v4.



## Dependency cleanup

Removes webapp dependencies left unused by this change: `seedrandom` and
`semver` (only the removed v3 code used them) plus a set that was
already dead, their orphaned `@types` packages, and two dead files. Adds
a `knip:deps` script and a `knip.json` config so unused dependencies can
be found the same way going forward.
This commit is contained in:
Eric Allam
2026-07-13 11:32:06 +01:00
committed by GitHub
parent c0f7c803b1
commit 5ba8557a51
114 changed files with 715 additions and 23184 deletions
-146
View File
@@ -1,146 +0,0 @@
#!/usr/bin/env node
const filename = process.argv[2];
if (!filename) {
console.error("Usage: analyze_marqs.mjs <filename>");
process.exit(1);
}
import fs from "fs/promises";
(async () => {
try {
const input = await fs.readFile(filename, "utf-8");
await processInput(input);
} catch (err) {
console.error(`Error reading file: ${err}`);
process.exit(1);
}
})();
// input is jsonl format, we want to split by line and then JSON parse each line
async function processInput(input) {
const rows = [];
const lines = input.split("\n");
for (const line of lines) {
if (!line) {
continue;
}
const row = JSON.parse(line);
// process each row
rows.push(row);
}
const queueChoiceCounts = {};
const queueMaxAges = {};
const queueMaxSizes = {};
const nextRangeOffsetCounts = {};
const consumerStats = {};
const rowsByConsumer = {};
console.log(`Processed ${rows.length} rows`);
// console.log(util.inspect(rows[0], { depth: 20 }));
for (const row of rows) {
const queueChoice = row.queueChoice;
if (queueChoice) {
if (!queueChoiceCounts[queueChoice]) {
queueChoiceCounts[queueChoice] = 0;
}
queueChoiceCounts[queueChoice]++;
}
}
for (const row of rows) {
rowsByConsumer[row.consumerId] = rowsByConsumer[row.consumerId] || [];
rowsByConsumer[row.consumerId].push(row);
const queueChoice = row.queueChoice;
if (!consumerStats[row.consumerId]) {
consumerStats[row.consumerId] = {
queueChoiceCounts: {},
totalQueueChoices: 0,
noQueueChoiceCount: 0,
};
}
if (queueChoice) {
const queueData = row.queuesWithScores.find((queue) => queue.queue === queueChoice);
console.log(
`[${row.timestamp}] -> ${queueChoice} [age:${queueData.age}] [size:${queueData.size}] [nextRange.offset=${row.nextRange.offset}] [queuesWithScores=${row.queuesWithScores.length}] [${row.consumerId}]`
);
if (!queueMaxAges[queueChoice] || queueData.age > queueMaxAges[queueChoice]) {
queueMaxAges[queueChoice] = queueData.age;
}
if (!queueMaxSizes[queueChoice] || queueData.size > queueMaxSizes[queueChoice]) {
queueMaxSizes[queueChoice] = queueData.size;
}
if (!consumerStats[row.consumerId].queueChoiceCounts[queueChoice]) {
consumerStats[row.consumerId].queueChoiceCounts[queueChoice] = 0;
}
consumerStats[row.consumerId].queueChoiceCounts[queueChoice]++;
consumerStats[row.consumerId].totalQueueChoices++;
} else {
console.log(
`[${row.timestamp}] -> No queue choice [nextRange.offset=${row.nextRange.offset}] [queuesWithScores=${row.queuesWithScores.length}] [${row.consumerId}]`
);
consumerStats[row.consumerId].noQueueChoiceCount++;
}
if (!nextRangeOffsetCounts[row.nextRange.offset]) {
nextRangeOffsetCounts[row.nextRange.offset] = 0;
}
nextRangeOffsetCounts[row.nextRange.offset]++;
}
console.log("Queue choice counts:");
console.log(queueChoiceCounts);
console.log("Queue max ages:");
console.log(queueMaxAges);
console.log("Queue max sizes:");
console.log(queueMaxSizes);
console.log("Next range offset counts:");
console.log(nextRangeOffsetCounts);
for (const consumerId in consumerStats) {
console.log(`Consumer ${consumerId}:`);
console.log(consumerStats[consumerId]);
}
for (const consumerId in rowsByConsumer) {
console.log(`\n## Consumer ${consumerId}:`);
for (const row of rowsByConsumer[consumerId]) {
const queueChoice = row.queueChoice;
if (queueChoice) {
const queueData = row.queuesWithScores.find((queue) => queue.queue === queueChoice);
console.log(
`[${row.timestamp}] -> ${queueChoice} [age:${queueData.age}] [size:${queueData.size}] [nextRange.offset=${row.nextRange.offset}] [queuesWithScores=${row.queuesWithScores.length}] [${row.consumerId}]`
);
} else {
console.log(
`[${row.timestamp}] -> No queue choice [nextRange.offset=${row.nextRange.offset}] [queuesWithScores=${row.queuesWithScores.length}] [${row.consumerId}]`
);
}
}
}
}