bug: a resumed run whose restore fails is requeued behind newer runs
Maintainer thường phản hồi trong vòng 1 ngày
Chưa có ai nhận issue này.
- #5114 của @granbellllc-png — đã đóng, không merge
Đánh giá
- Độ khó
- 4/5
- Thời gian dự kiến
- 3-5 ngày
- Mức phù hợp với người mới
- 48/100
- Loại issue
- Lỗi
- Độ rõ ràng
- Đặc tả rõ ràng
- Mức độ hoạt động
- Sôi nổi
- Công nghệ
- node.js, typescript
- Lĩnh vực
- backend, devtools, distributed-systems
Hướng nghiên cứu
Start with internal-packages/run-engine/src/run-queue/index.ts and trace #callNackMessage alongside tryNackAndRequeue, which handles checkpointed runs. Read the reproduction instructions and tests in the linked repro/resumed-run-queue-position branch, then compare the suggested fix with the direct nack paths in dequeueSystem.ts and engine/index.ts. Done means a regression test shows a failed restore keeps its queue position ahead of newer runs while repeated failures still back off.
Do mô hình lập chỉ mục viết ra từ nội dung của issue.
Mô tả
Provide environment information
System:
OS: Linux 6.8 Ubuntu 24.04.5 LTS 24.04.5 LTS (Noble Numbat)
CPU: (16) x64 AMD Ryzen 7 7700 8-Core Processor
Memory: 48.87 GB / 61.89 GB
Container: Yes
Shell: 5.2.21 - /bin/bash
Binaries:
Node: 24.22.1 - /usr/local/bin/node
Yarn: 1.22.22 - /usr/local/bin/yarn
npm: 11.19.1 - /usr/local/bin/npm
pnpm: 12.15.0 - /usr/local/bin/pnpm
The runs were on Trigger.dev Cloud: prod environment, us-east-1. Tasks were deployed with the trigger.dev CLI and @trigger.dev/sdk 4.5.14, runtime: "node-24". The output above is from the machine we reproduced on.
Describe the bug
A run that resumes after a wait is enqueued at its original queue position. continueRunIfUnblocked says so: "Re-enqueue with the original timestamp, so waiting runs dequeue before new runs." If the restore then fails before the run continues, the run is requeued through tryNackAndRequeue with no retry time. RunQueue then scores the message at now + backoff (internal-packages/run-engine/src/run-queue/index.ts, #callNackMessage). The run loses its position and waits behind every run that was triggered after it.
What we saw on 2026-11-06:
- Two parent runs called
batchTriggerAndWaitwith about 50 children each, and were checkpointed while they waited. Their queue hasconcurrencyLimit: 4and held a backlog of about 100 queued runs. - All children ended by 21:03 and 21:59 UTC. A few children were OOM-killed, and each of those was marked
CRASHED(TASK_PROCESS_OOM_KILLED) 300 s after it started. - Each parent was then requeued once, at 22:21 and 22:04 UTC. After that the API showed
status: "QUEUED",attempts: [], and a trace that ends atbatchTriggerAndWait(). In the dashboard the run looks as if it never started. - The parents stayed queued for over 70 minutes while runs created after them kept starting in the same queue. We cancelled them at 23:32 UTC. That was about 4.5 minutes before the last run that was queued ahead of them started, so we think they were waiting their turn, not lost.
- Each requeue freed a queue slot that another run took within 0.2 s. As far as we can tell from the code, only
tryNackAndRequeuewritesPENDINGfor a run that is resuming from a checkpoint.
Expected: a resume whose restore has to be retried keeps its place ahead of runs triggered after it, as the first resume does.
We can't see why the restores failed. The parents ran on a small machine and were close to its memory limit when they started waiting, so we think the restored process was OOM-killed. That part is our sizing. See "Additional information" for how such a restore is reported.
Reproduction repo
https://github.com/PlunderStruck/trigger.dev/tree/repro/resumed-run-queue-position
To reproduce
The branch adds one run-engine test file, explained in repro/resumed-run-queue-position/README.md. It uses containerTest and a real RunEngine. The tests assert today's behaviour, so they pass on main:
cd internal-packages/run-engine
pnpm run test ./src/engine/tests/resumedRunQueuePositionRepro.test.ts --run
Each test:
- Triggers a parent in a queue with concurrency limit 1, starts it, and blocks it on a waitpoint.
- Checkpoints it, then completes the waitpoint, so the parent is enqueued for a restore and dequeued ahead of newer runs, as designed.
- Triggers a newer run while the restore holds the slot.
- Fails the restore before
continueRunExecution, in one of two ways:- Stall. The restored runner never continues, so the
PENDING_EXECUTINGheartbeat requeues it. - Supervisor report. The supervisor reports the restore as
requeue, asclassifyRestoreFailuredoes for a Runner that failed with reasonOOMKilled.
- Stall. The restored runner never continues, so the
- Asserts that the parent is
PENDINGwith its checkpoint, that the next dequeue returns the newer run, and that the parent is dequeued only after the newer run finishes.
Suggested fix
These are ideas from reading the code; you know the trade-offs far better than we do.
- Keep the position on the first requeue of a resume.
tryNackAndRequeuealready knows whether the run has a checkpoint. It could asknackMessageto redeliver at the message's original score, as the resume enqueue does. Later requeues would keep the backoff, so a restore that keeps failing does not spend the 12 queue attempts in a tight loop. Cold starts would be unchanged. We put this on a branch with a regression test inrestoreOutcome.test.ts, which fails without the change: https://github.com/triggerdotdev/trigger.dev/compare/main...PlunderStruck:trigger.dev:fix/resumed-run-queue-position. It adds 18 lines torunAttemptSystem.tsandrun-queue/index.ts, most of them comments. We'd be glad to open it as a PR if that's welcome. - Not covered by that branch: the direct
runQueue.nackMessagecalls indequeueSystem.tsandengine/index.ts(error and admin paths) still requeue a resume atnow + backoff.
Additional information
- An OOM-killed restore is retried on the same machine with no reason recorded.
classifyRestoreFailure(apps/supervisor/src/workloadManager/runCrd.ts) mapsOOMKilledto its default{ outcome: "requeue" }. The runner-kill reporter also skips a run whose snapshot is notEXECUTING. The run therefore records no attempt and no error,retry.outOfMemorycan't move it to a larger machine, and each retry goes to the back of the queue until the 12 queue attempts are spent. MappingOOMKilledto a failure withTASK_PROCESS_OOM_KILLEDwould letretry.outOfMemoryapply. We haven't changed this, because we can't confirm the restore reason on Cloud. - No trace or attempt event for a requeue.
tryNackAndRequeueemits no run event. A run that has resumed and been requeued looks the same as one that has never started. - Related but different: #4971 and #4988 (the parent was never resumed) and #5060 (the run stayed
EXECUTINGafter its task finished). Here the parent is resumed but then requeued behind newer runs. - We can share the run IDs privately.
- Ngôn ngữ chính
- TypeScript
- Star
- 16.5k
- Fork
- 1.5k
- Merge trung bình
- 1 ngày 15 giờ
- Pull request đã merge (30 ngày)
- 17
Chuẩn bị môi trường
Dự án này không cung cấp dev container, Dockerfile hay hướng dẫn đóng góp, nên bạn cần tự thiết lập môi trường: hãy bắt đầu từ README và xem hướng dẫn đóng góp lần đầu của chúng tôi để biết các bước chung.
Bắt đầu từ đâu
- Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
- Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
- Fork repository và làm thay đổi trên một nhánh.
- Mở pull request có tham chiếu số hiệu của issue.
Issue khác của triggerdotdev/trigger.dev
-
bug: retry.fetch ignores the caller's abort signal and retries aborted requestsCó thể làm lại được Pull request cho issue này đã bị đóng mà không được merge. Đang mở
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 76/100
triggerdotdev/trigger.dev#5116 · 2 bình luận ·
Maintainer thường phản hồi trong vòng 1 ngày
-
bug: Chat transport crashes on Safari < 17.4: AbortSignal.any is not a functionCó thể làm lại được Pull request cho issue này đã bị đóng mà không được merge. Đang mở
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 83/100
triggerdotdev/trigger.dev#5098 · 2 bình luận ·
Maintainer thường phản hồi trong vòng 1 ngày
-
bug: Side menu items lose their active state on detail pagesCó thể làm lại được Pull request cho issue này đã bị đóng mà không được merge. Đang mở
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 72/100
triggerdotdev/trigger.dev#5088 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
bug: RBAC loader logs "plugin found but failed to load" when @triggerdotdev/plugins is simply not installedCó thể làm lại được @itsfaryal đã nhận 17 ngày trước và không có pull request nào đang mở. Đang mở
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 82/100
triggerdotdev/trigger.dev#5083 · 1 bình luận ·
Maintainer thường phản hồi trong vòng 1 ngày
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 83/100
triggerdotdev/trigger.dev#5072 · 1 bình luận ·
Maintainer thường phản hồi trong vòng 1 ngày
Tất cả issue của triggerdotdev/trigger.dev
Issue tương tự
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 72/100
Maintainer thường phản hồi trong vòng 1 ngày
-
bug platform-android
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 68/100
Termix-SSH/Mobile#146 · 1 bình luận ·
Maintainer thường phản hồi trong vòng 2 ngày
-
bug
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 66/100
devswha/herdr-web-ui#3875 ·
-
Độ khó 1/5 Dưới một giờ Mức phù hợp với người mới 88/100
-
area: runtime chore cli consistency dependencies developer-experience difficulty: easy discovery documentation enhancement good first issue help wanted hygiene json-output packaging priority: P3 tests version
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 78/100
Maintainer thường phản hồi trong vòng 1 ngày