Fix event-task leak with a counter + drain promise (WaitGroup) — alt to #15#16
Closed
henrikbjorn wants to merge 1 commit into
Closed
Fix event-task leak with a counter + drain promise (WaitGroup) — alt to #15#16henrikbjorn wants to merge 1 commit into
henrikbjorn wants to merge 1 commit into
Conversation
Alternative to the Set-based fix: instead of tracking in-flight event-hook tasks in a collection, keep only a count (@event_tasks) plus a one-shot drain Promise. Each event increments before spawning and decrements in its ensure; connection_closed sets @closing, and the last hook to finish (or connection_closed itself, if already idle) resolves the promise, which the drain awaits. Retention is a single integer for the whole life of the connection — nothing per-event is stored — vs the old Async::Barrier, which retained one task node per event until #wait (called only on disconnect), leaking ~GBs over days on a long-lived inbound connection subscribed to many events. vs the Set approach: no collection to snapshot/iterate on drain, and no first-error truncation of the drain loop. Same regression test (in-flight count returns to zero). Full suite green.
Member
Author
|
Closing in favour of #15 (self-reaping Set). Both fix the leak identically and pass the same test; #15 keeps less coordinated state (one collection that self-reaps, vs this PR's counter + drain-promise + closing-flag). Keeping the simpler one. #15 now uses |
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.
Alternative to #15 (opened so both approaches can be compared side by side). Same bug, same regression test, different drain mechanism.
The bug (same as #15)
Listener::Basedispatched every ESL event through one per-connectionAsync::Barrier.Barrier#asyncretains each task until#wait, and@event_barrier.waitis only called inconnection_closed(socket drop). So on a long-lived inbound connection every event permanently leaks a task node — ~GBs over days on a busy switch (theanswering_machine~8GB/2d).This approach: counter + drain Promise (WaitGroup)
Keep only a count of in-flight event tasks plus a one-shot drain
Async::Promise:Retention is one integer for the whole life of the connection — nothing per-event is stored.
vs the Set approach (#15)
Both fix the leak and pass the same regression test. This one:
.dup);Set'seach(&:wait)re-raises the first failure;Barrier#waithad the same gap).Promise(notCondition) is deliberate: a resolve that races ahead of the wait is still delivered, so shutdown ordering can't lose the signal.This mirrors the design already on the
spike/em-style-simplificationbranch (33f6fbd); this PR just lands it minimally onmaster. Full suite green (121 runs, 0 failures).