feat: add graceful shutdown with drain window and idle-connection grace period - #62
feat: add graceful shutdown with drain window and idle-connection grace period#62kaiburjack wants to merge 1 commit into
Conversation
daichirata
left a comment
There was a problem hiding this comment.
Thank you for adding graceful shutdown support! I left a few comments.
| select { | ||
| case <-tracker.noneOpen(): | ||
| slog.Info("shutdown: no connections remain") | ||
| case <-time.After(s.minCloseDelay): |
There was a problem hiding this comment.
Since this wait may end before minCloseDelay elapses, I wonder if naming this setting something like idleGracePeriod would better reflect its behavior. This would also apply to the corresponding CLI flag and documentation. What do you think?
There was a problem hiding this comment.
Yes, very good idea. That is definitely not a "minimum" delay.
There was a problem hiding this comment.
I've incorporated the suggested changes in the latest (squashed) commit.
| corsOrigin = flag.String("cors-origin", "", "Value for the Access-Control-Allow-Origin header.") | ||
| shutdownDelay = flag.Duration("shutdown-delay", 0, "Delay after SIGTERM/SIGINT during which requests are served normally but responses carry Connection: close.") | ||
| minCloseDelay = flag.Duration("shutdown-min-close-delay", 0, "After the listener closes, keep idle connections alive at least this long before closing them.") | ||
| shutdownTimeout = flag.Duration("shutdown-timeout", 0, "Max wait for in-flight requests after the min-close-delay; 0 waits indefinitely.") |
There was a problem hiding this comment.
With the current default, shutdown may wait indefinitely if an in-flight request gets stuck, for example while streaming to a slow client or waiting on GCS. Would a finite default such as 30 seconds be safer, while still allowing users to explicitly set 0 if they want to wait indefinitely? What do you think?
There was a problem hiding this comment.
Yes, setting it to something reasonable, like the proposed 30s is a good idea.
The other flags should keep their 0s default, though, IMO, since it is highly deployment-scenario-dependent what values make sense there, and we should keep the behaviour mostly compliant with the previous behaviour before these changes.
There was a problem hiding this comment.
I've incorporated the suggested changes in the latest (squashed) commit.
0e3212d to
6527a08
Compare
6527a08 to
fa4de1a
Compare
Motivation
Currently, when receiving SIGTERM (i.e. when used in Kubernetes), the Go HTTP server will immediately initiate shutdown, closing its listen socket and also all currently idle connections immediately.
This is problematic when doing a rolling update in Kubernetes, because:
The HTTP/1.1 RFC 9112 also briefly discusses this:
We have seen all of these problems in our Kubernetes production deployment of gcsproxy (and other services) and have forked it because of this (and because of many other features we would like to have): https://github.com/HBTGmbH/gcsproxy
Now, that the main gcsproxy project is actively being maintained again, we would like to add these features to the upstream project as well.
I've also written some blog posts about all of these problems :)
https://medium.com/hamburger-berater-team/achieving-zero-downtime-in-kubernetes-d9e3e5b5927c
What this PR adds
Connection: closeresponse header in the response, indicating to the client to not resuse this connection anymore, because the server is about to shutdown. Eventually, the random connection routing in iptables/netfilter will result in all such active connections being routed somewhere elseConnection: close, or the client closed it on their own, due to client-side idle/keep-alive timeout), the server is shutdown.