Repository navigation
fix(queue): run GitHub reaction lookups in parallel so the poll fits its timeout - #292
Merged
Merged
Conversation
…its timeout GITHUB_REACTION_POLL read reactions for each pending AI comment one at a time. Prod had 256 pending messages, and the serial loop overran the 30s job timeout on every attempt, so each scheduled run was dead-lettered. Keep up to 8 lookups in flight instead.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What failed in prod
On Oct 28 at 12:12:44 PM PDT, the prod worker (deployment 7947134d, commit 00dc276) dead-lettered job
cmvshmzp10002lq011ki9p02j(GITHUB_REACTION_POLL) after 5 of 5 attempts. The error wasJob timed out after 30000ms.The scheduler created this job when the worker booted at 12:09:01 PM PDT. This is not a leftover from the redeploys. The "fenced" lines come from the job itself: the timeout is a
Promise.race, so the handler keeps running after the timeout, and its late progress write is then fenced out. Each attempt logged[GithubReactionPoll] Checked 256 messagesafter it had already timed out. The same dead-letter shows up on every earlier worker deployment we have logs for (1 to 7 per deployment, with the pending count growing from 225 to 256).Cause
handleGithubReactionPollmade one GitHub request per pending AI comment, one at a time. The pending list only grows, because comments deleted on GitHub return 404 and stay pending. At about 240 ms per request, 256 requests take more than 30s, which is the default job timeout. This job type has no override.Fix
The handler now keeps up to 8 reaction lookups in flight (
GITHUB_REACTION_POLL_CONCURRENCY). Each message is still handled the same way: skips, 404 logging, POSITIVE/NEGATIVE updates and the ESCALATION job are unchanged.Local red/green proof (real surface)
Setup: the real
Worker(prod default 30s timeout, concurrency 1 for this type) and the real handler, loaded through@copilotkit/outpost/queuedist, against a local Postgres (pgvector pg16, all migrations applied). I seeded 256 pending AI-generated GITHUB_ISSUE messages. Their comment IDs are the 12 deleted-comment IDs from the prod log, plus 244 real CopilotKit/CopilotKit issue comment IDs. Reporter logins are placeholders. Reaction reads go to the real api.github.com. Only the GitHub App token exchange is stubbed locally.RED (base 00dc276), 2:01 PM PDT:
GREEN (this branch, same seed and same script), 2:02 PM PDT:
The 12 deleted comments were logged as 404s and skipped in both runs.
Tests
There are 3 new unit tests in
github-reaction-poll.test.ts. On the old code, 2 of them fail:On this branch, all 12 tests in the file pass. The third new test checks that +1, -1 and 404 outcomes are all still handled when the lookups run in parallel.
Local gates: lint and typecheck pass. Prettier passes on the changed files. Repo-wide
format:checkalready fails on base (276 files).pnpm testpasses exceptstarts the probe child cleanly, which fails on base 00dc276 too. That is local-only (Node 26 on this machine; the repo targets Node 24).