Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

pickfirst: Implement Happy Eyeballs #7725

Merged
merged 26 commits into from
Nov 12, 2024
Merged
Changes from 1 commit
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
db0dda7
Implement happy eyeballs
arjan-bal Oct 8, 2024
826bb03
Use timeAfterFunc
arjan-bal Oct 11, 2024
4e68e58
Address review comments
arjan-bal Oct 11, 2024
fe69816
Move timer func to internal, improve log statement and address review…
arjan-bal Oct 16, 2024
3022304
Remove env var
arjan-bal Oct 16, 2024
0a3ffd3
Change to e2e style test
arjan-bal Oct 16, 2024
6697267
Fix vet
arjan-bal Oct 16, 2024
67f7a1a
Fix vet
arjan-bal Oct 16, 2024
9712ec5
refactor test
arjan-bal Oct 16, 2024
6c8fb41
Avoid creating a context
arjan-bal Oct 16, 2024
04a912f
address review comments
arjan-bal Oct 17, 2024
0bb745f
Use BlockingDialer instead of implementing a hanging server
arjan-bal Oct 17, 2024
99e2e89
Fix test synchronization
arjan-bal Oct 17, 2024
592ba0d
Test refactorings
arjan-bal Oct 18, 2024
84d6ed4
Cancel timer when processing new resolver update
arjan-bal Oct 19, 2024
8f63d8e
Improve whitespaces and comments
arjan-bal Oct 23, 2024
09f27c6
Merge branch 'master' of github.com:grpc/grpc-go into grpc-go-happy-e…
arjan-bal Oct 23, 2024
6610516
Refactor fake timer
arjan-bal Oct 23, 2024
d6bc007
Don't use expired context
arjan-bal Oct 23, 2024
19a3165
Remove unnecessary timer in test
arjan-bal Oct 23, 2024
598fdd0
Address review comments
arjan-bal Oct 24, 2024
d3bde50
Merge remote-tracking branch 'source/master' into grpc-go-happy-eyeballs
arjan-bal Nov 6, 2024
8b4b28e
Remove stale comment
arjan-bal Nov 6, 2024
6c16943
Use rand/v2
arjan-bal Nov 7, 2024
11fe515
Address review comments
arjan-bal Nov 8, 2024
5c4ff49
Rename to connectionFailedInFirstPass
arjan-bal Nov 12, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Move timer func to internal, improve log statement and address review…
… comments
arjan-bal committed Oct 16, 2024
commit fe69816f64cee34ae7d853e761221deb1b541b3a
14 changes: 11 additions & 3 deletions balancer/pickfirst/internal/internal.go
Original file line number Diff line number Diff line change
@@ -18,7 +18,15 @@
// Package internal contains code internal to the pickfirst package.
package internal

import "math/rand"
import (
"math/rand"
"time"
)

// RandShuffle pseudo-randomizes the order of addresses.
var RandShuffle = rand.Shuffle
var (
// RandShuffle pseudo-randomizes the order of addresses.
RandShuffle = rand.Shuffle
// TimeAfterFunc allows mocking the timer for testing connection delay
// related functionality.
TimeAfterFunc = time.AfterFunc
)
10 changes: 4 additions & 6 deletions balancer/pickfirst/pickfirstleaf/pickfirstleaf.go
Original file line number Diff line number Diff line change
@@ -58,9 +58,6 @@ var (
// It is changed to "pick_first" in init() if this balancer is to be
// registered as the default pickfirst.
Name = "pick_first_leaf"
// timerAfterFunc allows mocking the timer for testing connection delay
// related functionality.
timerAfterFunc = time.AfterFunc
)

const (
@@ -438,25 +435,26 @@ func (b *pickfirstBalancer) scheduleNextConnectionLocked() {
if !envconfig.PickFirstHappyEyeballsEnabled {
return
}
curAddr := b.addressList.currentAddress()
b.cancelScheduled()
ctx, cancel := context.WithCancel(context.Background())
closeFn := timerAfterFunc(connectionDelayInterval, func() {
closeFn := internal.TimeAfterFunc(connectionDelayInterval, func() {
b.mu.Lock()
defer b.mu.Unlock()
// If the scheduled task is cancelled while acquiring the mutex, return.
if ctx.Err() != nil {
return
}
if b.logger.V(2) {
b.logger.Infof("Happy Eyeballs timer expired.")
b.logger.Infof("Happy Eyeballs timer expired while waiting for connection to %q.", curAddr.Addr)
}
if b.addressList.increment() {
b.requestConnectionLocked()
}
}).Stop
b.cancelScheduled = sync.OnceFunc(func() {
closeFn()
cancel()
closeFn()
})
}

21 changes: 11 additions & 10 deletions balancer/pickfirst/pickfirstleaf/pickfirstleaf_test.go
Original file line number Diff line number Diff line change
@@ -27,6 +27,7 @@ import (

"google.golang.org/grpc/attributes"
"google.golang.org/grpc/balancer"
"google.golang.org/grpc/balancer/pickfirst/internal"
"google.golang.org/grpc/connectivity"
"google.golang.org/grpc/internal/envconfig"
"google.golang.org/grpc/internal/grpctest"
@@ -261,8 +262,8 @@ func (s) TestPickFirstLeaf_HappyEyeballs_TriggerConnectionDelay(t *testing.T) {
}()

timerCh := make(chan struct{})
originalTimer := timerAfterFunc
timerAfterFunc = func(_ time.Duration, f func()) *time.Timer {
originalTimer := internal.TimeAfterFunc
internal.TimeAfterFunc = func(_ time.Duration, f func()) *time.Timer {
// Set a really long expiration to prevent it from triggering
// automatically.
ret := time.AfterFunc(time.Hour, f)
@@ -277,7 +278,7 @@ func (s) TestPickFirstLeaf_HappyEyeballs_TriggerConnectionDelay(t *testing.T) {
}

defer func() {
timerAfterFunc = originalTimer
internal.TimeAfterFunc = originalTimer
}()

cc := testutils.NewBalancerClientConn(t)
@@ -358,8 +359,8 @@ func (s) TestPickFirstLeaf_HappyEyeballs_TFAfterEndOfList(t *testing.T) {
}()

timerCh := make(chan struct{})
originalTimer := timerAfterFunc
timerAfterFunc = func(_ time.Duration, f func()) *time.Timer {
originalTimer := internal.TimeAfterFunc
internal.TimeAfterFunc = func(_ time.Duration, f func()) *time.Timer {
// Set a really long expiration to prevent it from triggering
// automatically.
ret := time.AfterFunc(time.Hour, f)
@@ -374,11 +375,11 @@ func (s) TestPickFirstLeaf_HappyEyeballs_TFAfterEndOfList(t *testing.T) {
}

defer func() {
timerAfterFunc = originalTimer
internal.TimeAfterFunc = originalTimer
}()

defer func() {
timerAfterFunc = originalTimer
internal.TimeAfterFunc = originalTimer
}()

cc := testutils.NewBalancerClientConn(t)
@@ -505,8 +506,8 @@ func (s) TestPickFirstLeaf_HappyEyeballs_TFThenTimerFires(t *testing.T) {

timerMu := sync.Mutex{}
timerCh := make(chan struct{})
originalTimer := timerAfterFunc
timerAfterFunc = func(_ time.Duration, f func()) *time.Timer {
originalTimer := internal.TimeAfterFunc
internal.TimeAfterFunc = func(_ time.Duration, f func()) *time.Timer {
// Set a really long expiration to prevent it from triggering
// automatically.
ret := time.AfterFunc(time.Hour, f)
@@ -524,7 +525,7 @@ func (s) TestPickFirstLeaf_HappyEyeballs_TFThenTimerFires(t *testing.T) {
}

defer func() {
timerAfterFunc = originalTimer
internal.TimeAfterFunc = originalTimer
}()

cc := testutils.NewBalancerClientConn(t)