fix(eventstream): M9 broker leaks fd + goroutine when a subscriber is evicted mid-publish - #34
Merged
Merged
Conversation
…ter it A subscriber dropped after repeated publish failures was removed from the topic map and nothing else. Such a peer is by definition not draining, so its handleConn goroutine stayed parked in ReadEvent and any writer still blocked on the same stream stayed blocked — leaking the goroutine, the descriptor and the rate-bucket entry for the lifetime of the daemon. Eviction now also drops the rate bucket and closes the stream, which returns both goroutines through their normal teardown. subscriber.Close tolerates a nil stream, matching remote(). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
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.
Addresses finding M9.
M9 — evicted subscriber leaks its descriptor, goroutine and rate bucket
service.goWhen
publishWithdrops a subscriber aftermaxConsecutivePublishFailures, it calledb.removeSub(s)and nothing else. That subscriber is by definition a peer that is not draining, so:handleConngoroutine stays parked inReadEventon a stream nobody will ever write to — it never reaches the deferred teardown, so the descriptor is held for the lifetime of the daemon;publishWriteTimeoutselect abandons) stays blocked inwrite(s, evt)on the same stuck stream, and a fresh one accumulates on each publish until the eviction threshold is reached;b.rate[s]is never deleted, so the rate map grows without bound.Eviction now performs the full teardown —
removeSub+forgetPublisher+Close— and closing the stream is what returns both goroutines through their normal paths.subscriber.Closenow tolerates a nil stream, matching the existing nil handling inremote(); broker unit tests construct subscribers without one.Tests
zz_evict_teardown_test.go— fails against the pre-fix eviction loop (handleConn still parked after eviction), passes here. Drives a realnet.Pipe-backed subscriber that subscribes then goes quiet, fails it past the tolerance, and assertshandleConnreturns, the rate bucket is dropped, and the stream is closed.No wire-format or protocol changes.
🤖 Generated with Claude Code