aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDan Peterson <dpiddy@gmail.com>2020-10-22 19:25:56 -0300
committerEmmanuel Odeke <emmanuel@orijtech.com>2020-10-27 18:38:48 +0000
commitf0c9ae5452832f0f9e4dfa38f756ae9137577482 (patch)
tree865569ff56bad1e8b8a0d5c50dc89e2aaeaa2989
parentc3c6fbf31419d37b0ae7d99b5378f6f8e9080b24 (diff)
downloadgo-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.go5
-rw-r--r--src/net/http/server.go28
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())
}
}
}