-
-
Notifications
You must be signed in to change notification settings - Fork 4.2k
fix: SSE handler blocking #5181
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
Fix SSE handler blocking by ensuring the producer goroutine closes the channel and the consumer loop exits cleanly when the stream ends.
- Move close(client) into the producer goroutine to signal completion.
- Add ok check on channel receive to exit the handler when the channel is closed.
for { | ||
select { | ||
case data := <-client: | ||
case data, ok := <-client: | ||
if !ok { | ||
return | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[nitpick] Since the select only has a single receive case, consider simplifying to a for-range loop over the channel to remove the manual ok check and make the control flow clearer: for data := range client { ... }.
Copilot uses AI. Check for mistakes.
case data, ok := <-client: | ||
if !ok { | ||
return | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please add a test that generates an SSE handler and verifies it exits when the producer completes (channel close) and does not block or leak goroutines. This change introduces important lifecycle behavior that should be covered by a test (e.g., a mocked logic that sends N events then returns, asserting the handler returns and no goroutines remain running).
Copilot generated this review using guidance from repository custom instructions.
Co-authored-by: Copilot <[email protected]>
#5182
#5155
#5094
close(client)
call is placed in the top-levelhttp.HandlerFunc
'sdefer
, which means the channel is only closed when the entire HTTP request handler returns.l.{{.Call}}
) runs in a separatethreading.GoSafeCtx
goroutine. When this goroutine finishes sending all events, theclient
channel remains open becauseclose(client)
is not executed within its scope.for
loop, which reads from theclient
channel, will then block indefinitely oncase data := <-client:
because it never receives a "closed" signal, even though no more events will be sent.case data := <-client:
lacks anok
check, which is a standard Go practice to detect if a channel has been closed, preventing unnecessary processing of zero values.This results in:
for
loop goroutine remains active indefinitely, consuming resources, even after all events have been sent.