Skip to content
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

Fix panic on Shutdown when there is at least one active connection #1

Merged
merged 1 commit into from
Jun 12, 2023

Conversation

hotafrika
Copy link
Owner

@hotafrika hotafrika commented Jun 9, 2023

This is the fix for the "panic on sse-server shutdown when there is at least one active connection" issue.
It does the following: 1) after calling the Shutdown() function it doesn't allow new sse-connections to be established 2) it avoids sending to already closed removeClient channel

@@ -128,7 +138,7 @@ func (s *Server) Restart() {

// Shutdown performs a graceful server shutdown.
func (s *Server) Shutdown() {
s.shutdown <- true
close(s.shutdown)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why does this need to change? If no message is sent to the shutdown channel, how will the listeners know the shutdown has occurred? (e.g. ln.80)

Copy link
Owner Author

@hotafrika hotafrika Jun 11, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Point №1: Actually, we never need the value sent to the shutdown channel. Actually, we can change the type of this channel from chan bool to chan struct{}.
Point №2: When we close the channel, then all the readers from this channel (<-s.shutdown) receive the default value (in this case false value).

Conclusion: closing the shutdown channel will work as a mechanism of informing all the goroutines about the sse server shutdown process.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah oh, I am not experienced in using channels and I didn't know the default value got sent when a channel is closed.

@@ -255,7 +265,6 @@ func (s *Server) dispatch() {
close(s.addClient)
close(s.removeClient)
close(s.closeChannel)
close(s.shutdown)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why did this move to shutdown function? The existing behaviour made sense to me.

  1. Shutdown function is called which sends an event to the shudown channel
  2. dispatch function receives the shutdown event and then closes the channel as no further events are expected

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As you can see, in this PR all the serving goroutines (ServerHTTP) use reading from the s.shutdown channel. At the same time, thedispatch function also reads from this channel. It means, that if we just send some value to this channel, we don't know where this value will be read: in the dispatch function or in the one of running ServeHTTP functions. If we close this channel, then all the functions will receive default values by reading from this channel.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@insano10 Please, take a look at another PR (#2) where I added a unit test for the entire shutdown flow. This test fails with the current implementation and succeeds with the updated implementation.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for the explanation, that makes sense now :)

Copy link

@insano10 insano10 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@hotafrika hotafrika merged commit 75eb8c1 into master Jun 12, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants