-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Some grpc-go load balancing policies, including round_robin, do not take care about retrying resolving after an error is reported[^1]. This causes that when querying consul fails the grpc connection can hang in a state without addresses until the connection idle timeout expires, despite consul became reachable. To prevent it, retry periodically querying consul when it fails. This is done by creating a timer that calls ResolveNow() after it expires. The backoff implementation from commit f1f1a15 is recovered and modified to randomize subtracting or adding the jitter from the delay, use smaller pauses and returning the default implementation from a constructor instead of defining it as package variable. [^1]: grpc/grpc-go#7729
- Loading branch information
Showing
5 changed files
with
200 additions
and
30 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
package consul | ||
|
||
import ( | ||
"math/rand" | ||
"time" | ||
) | ||
|
||
type backoff struct { | ||
intervals []time.Duration | ||
jitterPct int | ||
jitterSrc *rand.Rand | ||
} | ||
|
||
func defaultBackoff() *backoff { | ||
return &backoff{ | ||
intervals: []time.Duration{ | ||
10 * time.Millisecond, | ||
50 * time.Millisecond, | ||
250 * time.Millisecond, | ||
500 * time.Millisecond, | ||
time.Second, | ||
2 * time.Second, | ||
3 * time.Second, | ||
4 * time.Second, | ||
5 * time.Second, | ||
}, | ||
|
||
jitterPct: 10, | ||
jitterSrc: rand.New(rand.NewSource(time.Now().UnixNano())), | ||
} | ||
} | ||
|
||
func (b *backoff) Backoff(retry int) time.Duration { | ||
idx := retry | ||
|
||
if idx < 0 || idx > len(b.intervals)-1 { | ||
idx = len(b.intervals) - 1 | ||
} | ||
|
||
d := b.intervals[idx] | ||
|
||
if b.jitterSrc.Intn(2) == 0 { | ||
return d + time.Duration(((int64(d) / 100) * int64((b.jitterSrc.Intn(b.jitterPct))))) | ||
} | ||
|
||
return d - time.Duration(((int64(d) / 100) * int64((b.jitterSrc.Intn(b.jitterPct))))) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
package consul | ||
|
||
import ( | ||
"fmt" | ||
"testing" | ||
"time" | ||
) | ||
|
||
func minBackoff(t time.Duration, jitterPCT int) time.Duration { | ||
return t - t*time.Duration(jitterPCT)/100 | ||
} | ||
|
||
func maxBackoff(t time.Duration, jitterPCT int) time.Duration { | ||
return t + t*time.Duration(jitterPCT)/100 | ||
} | ||
|
||
func TestBackoff_IntervalIdxBounds(t *testing.T) { | ||
b := defaultBackoff() | ||
|
||
for i := range []int{-100000, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 99999999} { | ||
t.Run(fmt.Sprintf("retry-%d", i), func(t *testing.T) { | ||
d := b.Backoff(-10) | ||
|
||
minB := minBackoff(b.intervals[len(b.intervals)-1], b.jitterPct) | ||
maxB := maxBackoff(b.intervals[len(b.intervals)-1], b.jitterPct) | ||
|
||
if d < minB { | ||
t.Errorf("backoff is %s, expecting >%s", d, minB) | ||
} | ||
|
||
if d > maxB { | ||
t.Errorf("backoff is %s, expecting <%s", d, minB) | ||
} | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters