]> Cypherpunks repositories - gostls13.git/commitdiff
net/http: use exponential backoff for polling in Server.Shutdown
authorDan Peterson <dpiddy@gmail.com>
Thu, 22 Oct 2020 22:25:56 +0000 (19:25 -0300)
committerEmmanuel Odeke <emmanuel@orijtech.com>
Tue, 27 Oct 2020 18:38:48 +0000 (18:38 +0000)
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>

src/net/http/http_test.go
src/net/http/server.go

index 49c2b4196ae7765fc7ebfa0a63887bed06e42711..3f1d7cee710ed2601ee616fb2d2a2f7e53826cbe 100644 (file)
@@ -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
index ba473d14f5bcce4374d1b37b97f9cd8d3779889a..4776d960e575883e95e1956c134f4be2c4834b05 100644 (file)
@@ -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())
                }
        }
 }