Refill freed concurrency slots continuously instead of per batch#41
Merged
nicnocquee merged 1 commit intoJun 12, 2026
Merged
Conversation
startInBackground processed jobs in fixed batches behind a barrier: it claimed up to a batch, then waited for every job in that batch to settle before claiming again. A single slow job left the other concurrency slots idle until it finished. With groupConcurrency it stalled the whole group, because each claim is capped below the number of ready jobs, so the rest never got claimed until the in-flight batch fully drained. Replace the batch-barrier loop with a continuous worker pool that keeps up to `concurrency` jobs in flight and refills each slot the moment it frees, never exceeding groupConcurrency. A periodic tick still enqueues due cron jobs and polls for new work every pollInterval, and stopAndDrain now waits on the set of in-flight jobs. No public API change. The one-shot start() path is untouched. Add a regression test that reproduces the group stall: a slow job plus several fast jobs in one group under groupConcurrency. It times out on the old barrier and passes with the continuous pool.
👷 Deploy request for dataqueue-demo pending review.Visit the deploys page to approve it
|
✅ Deploy Preview for docsdataqueuedev ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
nicnocquee
approved these changes
Jun 12, 2026
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #41 +/- ##
==========================================
- Coverage 80.18% 75.22% -4.97%
==========================================
Files 39 22 -17
Lines 7515 5982 -1533
Branches 987 896 -91
==========================================
- Hits 6026 4500 -1526
+ Misses 1463 1455 -8
- Partials 26 27 +1
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
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.
Summary
startInBackgroundprocessed jobs in fixed batches behind a barrier. It claimed a batch, then waited for every job in that batch to settle before claiming again. A single slow job left the other concurrency slots idle until it finished. WithgroupConcurrencyset it stalled the whole group, because each claim is capped below the number of ready jobs, so the rest were never claimed until the in-flight batch fully drained.This replaces the batch barrier with a continuous worker pool.
The problem
With
concurrency: 3andgroupConcurrency: 3, a queue of same-group jobs is claimed 3 at a time. The old loop awaited all 3 before polling again:So if 2 of the 3 finish quickly but the third runs for minutes, the two freed slots sit idle and the remaining queued jobs are never picked up until the slow one completes or times out. The claim SQL already supports incremental per-group refill (it subtracts the in-flight count per group), but the loop never re-polled mid-batch, so that capability went unused.
The fix
startInBackgroundnow runs a continuous pool:concurrencyjobs in flight and refill each slot the instant it frees (from the job'sfinally), instead of waiting for the batch.concurrencyorgroupConcurrency.pollIntervalstill runs the cron-enqueue hook and picks up newly added jobs.stopAndDrainwaits on the set of in-flight job promises.Compatibility
start()(serverless) path is untouched.concurrencynow behaves as a steady in-flight cap in background mode, which is what the docs implied. The doc comments were updated to match.Testing
Added a regression test in
processor.test.ts: a slow job plus several fast jobs in one group undergroupConcurrency. It times out on the old barrier (the group stalls behind the slow job) and passes with the pool, which also confirmsgroupConcurrencystays honored. The full package test suite passes locally against Postgres and Redis.