diff --git a/rpc/lib/client/ws_client.go b/rpc/lib/client/ws_client.go index d233004b..3a867381 100644 --- a/rpc/lib/client/ws_client.go +++ b/rpc/lib/client/ws_client.go @@ -4,7 +4,7 @@ import ( "context" "encoding/json" "fmt" - "math" + "math/rand" "net" "net/http" "sync" @@ -254,11 +254,13 @@ func (c *WSClient) reconnect() error { c.mtx.Unlock() }() + _1sAsNs := float64(time.Second.Nanoseconds()) for { - c.Logger.Info("reconnecting", "attempt", attempt+1) + jitter := time.Duration(rand.Float64() * _1sAsNs) + backoffDuration := jitter + ((1 << uint(attempt)) * time.Second) - d := time.Duration(math.Exp2(float64(attempt))) - time.Sleep(d * time.Second) + c.Logger.Info("reconnecting", "attempt", attempt+1, "backoff_duration", backoffDuration) + time.Sleep(backoffDuration) err := c.dial() if err != nil { diff --git a/rpc/lib/client/ws_client_test.go b/rpc/lib/client/ws_client_test.go index f5aa027f..fecbee7a 100644 --- a/rpc/lib/client/ws_client_test.go +++ b/rpc/lib/client/ws_client_test.go @@ -1,11 +1,14 @@ package rpcclient import ( + "bytes" "context" "encoding/json" + "errors" "net" "net/http" "net/http/httptest" + "regexp" "sync" "testing" "time" @@ -191,3 +194,55 @@ func callWgDoneOnResult(t *testing.T, c *WSClient, wg *sync.WaitGroup) { } } } + +func TestWSClientReconnectWithJitter(t *testing.T) { + if testing.Short() { + t.Skipf("This is a potentially long test") + } + + n := 8 + maxReconnectAttempts := 3 + // Max wait time is ceil(1+0.999) + ceil(2+0.999) + ceil(4+0.999) + ceil(...) = 2 + 3 + 5 = 10s + ... + maxSleepTime := time.Second * time.Duration(((1<