Skip to content

Conversation

wuqinqiang
Copy link
Contributor

@wuqinqiang wuqinqiang commented Sep 19, 2025

#5182
#5155
#5094

  1. The close(client) call is placed in the top-level http.HandlerFunc's defer, which means the channel is only closed when the entire HTTP request handler returns.
  2. The actual event generation (l.{{.Call}}) runs in a separate threading.GoSafeCtx goroutine. When this goroutine finishes sending all events, the client channel remains open because close(client) is not executed within its scope.
  3. The main for loop, which reads from the client channel, will then block indefinitely on case data := <-client: because it never receives a "closed" signal, even though no more events will be sent.
  4. Furthermore, the case data := <-client: lacks an ok check, which is a standard Go practice to detect if a channel has been closed, preventing unnecessary processing of zero values.

This results in:

  • Goroutine Leak: The main for loop goroutine remains active indefinitely, consuming resources, even after all events have been sent.
  • Blocking Behavior: SSE connections do not terminate gracefully after event completion, holding clients in a waiting state.

Copy link

codecov bot commented Sep 19, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@wuqinqiang wuqinqiang changed the title fix: SSE handler template blocking fix: SSE handler blocking Sep 19, 2025
@kevwan kevwan requested review from Copilot and kesonan September 20, 2025 14:38
Copy link
Contributor

@Copilot Copilot AI left a 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.

Comment on lines 45 to +51
for {
select {
case data := <-client:
case data, ok := <-client:
if !ok {
return
}
Copy link
Preview

Copilot AI Sep 20, 2025

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.

Comment on lines +48 to +51
case data, ok := <-client:
if !ok {
return
}
Copy link
Preview

Copilot AI Sep 20, 2025

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.

@kevwan kevwan self-assigned this Sep 20, 2025
@kesonan kesonan added the goctl/approved Categorizes PR was already approved, it's already be merged. label Sep 21, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
goctl/approved Categorizes PR was already approved, it's already be merged.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants