diff options
author | Dan Peterson <dpiddy@gmail.com> | 2020-10-22 19:25:56 -0300 |
---|---|---|
committer | Emmanuel Odeke <emmanuel@orijtech.com> | 2020-10-27 18:38:48 +0000 |
commit | f0c9ae5452832f0f9e4dfa38f756ae9137577482 (patch) | |
tree | 865569ff56bad1e8b8a0d5c50dc89e2aaeaa2989 | |
parent | c3c6fbf31419d37b0ae7d99b5378f6f8e9080b24 (diff) | |
download | go-f0c9ae5452832f0f9e4dfa38f756ae9137577482.tar.gz go-f0c9ae5452832f0f9e4dfa38f756ae9137577482.zip |
net/http: use exponential backoff for polling in Server.Shutdown
Instead of always polling 500ms, start with an interval of 1ms and
exponentially back off to at most 500ms. 10% jitter is added to each
interval.
This makes Shutdown more responsive when connections and listeners
close quickly.
Also removes the need for the polling interval to be changed in tests
since if tests' connections and listeners close quickly Shutdown will
also return quickly.
Fixes #42156
Change-Id: I5e59844a2980c09adebff57ae8b58817965e6db4
Reviewed-on: https://go-review.googlesource.com/c/go/+/264479
Run-TryBot: Emmanuel Odeke <emmanuel@orijtech.com>
Reviewed-by: Emmanuel Odeke <emmanuel@orijtech.com>
Trust: Emmanuel Odeke <emmanuel@orijtech.com>
Trust: Bryan C. Mills <bcmills@google.com>
-rw-r--r-- | src/net/http/http_test.go | 5 | ||||
-rw-r--r-- | src/net/http/server.go | 28 |
2 files changed, 21 insertions, 12 deletions
diff --git a/src/net/http/http_test.go b/src/net/http/http_test.go index 49c2b4196a..3f1d7cee71 100644 --- a/src/net/http/http_test.go +++ b/src/net/http/http_test.go @@ -13,13 +13,8 @@ import ( "os/exec" "reflect" "testing" - "time" ) -func init() { - shutdownPollInterval = 5 * time.Millisecond -} - func TestForeachHeaderElement(t *testing.T) { tests := []struct { in string diff --git a/src/net/http/server.go b/src/net/http/server.go index ba473d14f5..4776d960e5 100644 --- a/src/net/http/server.go +++ b/src/net/http/server.go @@ -15,6 +15,7 @@ import ( "fmt" "io" "log" + "math/rand" "net" "net/textproto" "net/url" @@ -2691,14 +2692,14 @@ func (srv *Server) Close() error { return err } -// shutdownPollInterval is how often we poll for quiescence -// during Server.Shutdown. This is lower during tests, to -// speed up tests. +// shutdownPollIntervalMax is the max polling interval when checking +// quiescence during Server.Shutdown. Polling starts with a small +// interval and backs off to the max. // Ideally we could find a solution that doesn't involve polling, // but which also doesn't have a high runtime cost (and doesn't // involve any contentious mutexes), but that is left as an // exercise for the reader. -var shutdownPollInterval = 500 * time.Millisecond +const shutdownPollIntervalMax = 500 * time.Millisecond // Shutdown gracefully shuts down the server without interrupting any // active connections. Shutdown works by first closing all open @@ -2731,8 +2732,20 @@ func (srv *Server) Shutdown(ctx context.Context) error { } srv.mu.Unlock() - ticker := time.NewTicker(shutdownPollInterval) - defer ticker.Stop() + pollIntervalBase := time.Millisecond + nextPollInterval := func() time.Duration { + // Add 10% jitter. + interval := pollIntervalBase + time.Duration(rand.Intn(int(pollIntervalBase/10))) + // Double and clamp for next time. + pollIntervalBase *= 2 + if pollIntervalBase > shutdownPollIntervalMax { + pollIntervalBase = shutdownPollIntervalMax + } + return interval + } + + timer := time.NewTimer(nextPollInterval()) + defer timer.Stop() for { if srv.closeIdleConns() && srv.numListeners() == 0 { return lnerr @@ -2740,7 +2753,8 @@ func (srv *Server) Shutdown(ctx context.Context) error { select { case <-ctx.Done(): return ctx.Err() - case <-ticker.C: + case <-timer.C: + timer.Reset(nextPollInterval()) } } } |