Repository navigation
fix(quota-share): give each in-flight request its own lease - #11547
Merged
diegosouzapw merged 2 commits intoAug 26, 2026
Merged
diegosouzapw merged 2 commits into
diegosouzapw merged 2 commits into
Conversation
The in-flight counter is what P2C reads to pick the less-loaded connection,
and its leak bound did not hold.
A slot carried ONE expiry per connection, and incrementInflight() refreshed
it on every increment. So a request that aborted before its release callback
ran kept its count alive for as long as the connection kept taking traffic —
the leak was unbounded precisely when the connection was busy, which is when
P2C's view of load matters. The module docstring promised the opposite: "the
counter can never leak forever, even without touching the generic dispatch".
Demonstrated with the injectable clock: one orphaned increment, then a
request every 60s that increments and releases correctly. Ten minutes on —
five times the 120s lease — the orphan still counted 1.
Each request now carries its own expiry and the count is the number of live
leases, so an orphan ages out on its own schedule regardless of neighbours.
Retiring on release needs a rule, since a release does not say which request
settled:
1. If any lease has already expired, retire the newest of those. A request
that outlived its own lease is the longest-running one, so an expired
lease is the best match for the caller, and consuming it leaves live
neighbours alone. Retiring a live lease instead would decrement twice
for one settled request.
2. Otherwise retire the newest live lease — never the oldest, or a normal
release would retire an orphan's lease and leave its own behind, and the
orphan would never age out.
Rule 1 exists because the first attempt at this got it wrong: retiring an
expired lease AND popping a live one made a three-concurrent-request case
report 1 where the previous code correctly reported 2. That case is now
pinned by the probe in the PR description and matches the old behaviour.
Bounded at 4096 leases per connection; beyond that the oldest is dropped,
which can only under-count an already-saturated connection — the fail-open
direction this module takes everywhere else.
Tests: 3 added. Two fail against the current implementation; the third
documents neighbour-preservation, which already held and now holds by
construction. quota-share 53, combo 162, quota-wide 1424 — all green.
Typecheck clean.
muhamadgalihsaputra
pushed a commit
to niyatna/NiyatnaRoute
that referenced
this pull request
Sep 27, 2026
…zapw#11547) Merged via /merge-batch (lote 2026-08-26, v3.8.51). Boarded no worktree combinado junto com outras ~30 PRs; validação única: typecheck/complexity/cognitive-complexity/changelog-integrity verdes, file-size rebaseado onde necessário (crescimento legítimo), lint com os mesmos 228 achados pré-existentes confirmados via sonda contra o tip puro (não introduzidos por este lote), e ~370 testes focados (unit + vitest) passando. Obrigado pela contribuição.
3 tasks
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
The quota-share in-flight counter is what P2C reads to pick the less-loaded connection, and its leak bound did not hold.
A slot carried one expiry per connection, and
incrementInflight()refreshed it on every increment. So a request that aborted before its release callback ran kept its count alive for as long as the connection kept taking traffic — the leak was unbounded precisely when the connection was busy, which is when P2C's view of load matters most. The module docstring promises the opposite:Reproduction
Driven through the injectable clock, so it is deterministic. One orphaned increment, then a request every 60s that increments and releases correctly:
Ten minutes on — five times the 120s
DEFAULT_LEASE_MS— the orphan still counts. Every later increment pushed the sharedexpiresAtMsforward, so it never aged out.Fix
Each request now carries its own expiry and the count is the number of live leases, so an orphan ages out on its own schedule regardless of what its neighbours do.
Retiring on release needs a rule, because a release does not say which request settled:
Rule 1 exists because my first attempt got this wrong, and I would rather show that than hide it. Retiring expired leases and then popping a live one made this case report 1 where the previous code correctly reported 2:
Bounded at 4096 leases per connection; past that the oldest is dropped, which can only under-count an already-saturated connection — the fail-open direction this module takes everywhere else (
getInflightreturns 0 for anything unknown).Scope, and what I am not claiming
I originally thought a second bug was here — that
decrementInflightdeleting the whole entry on an expired slot would zero out live neighbours. It does not, in practice. Because the shared expiry was refreshed by every increment, it could only lapse after a full lease with no traffic, by which point there are no live neighbours to lose. I checked before writing it up rather than after. The only defect fixed here is the leak.Tests
Three added to
tests/unit/quota-share-strategy.test.ts. Two fail against the current implementation, verified by swapping the base file in and confirming the swap took effect before running:The third pins neighbour-preservation, which already held and now holds by construction — flagging that so it is not miscounted as a third regression caught.
Full runs on this base:
quota-share-strategy34/34,tests/unit/combo/*162/162,tests/unit/*quota*1424 green,tsc -p tsconfig.typecheck-core.jsonclean.Notes
release/v3.8.51(the current default).