Skip to content

Commit 0066bf6

Browse files
authored
grpc: perform graceful switching of LB policies in the ClientConn by default (#5285)
1 parent 3cccf6a commit 0066bf6

8 files changed

+438
-423
lines changed

balancer_conn_wrappers.go

+218-113
Large diffs are not rendered by default.

clientconn.go

+14-79
Original file line numberDiff line numberDiff line change
@@ -278,15 +278,15 @@ func DialContext(ctx context.Context, target string, opts ...DialOption) (conn *
278278
if creds := cc.dopts.copts.TransportCredentials; creds != nil {
279279
credsClone = creds.Clone()
280280
}
281-
cc.balancerBuildOpts = balancer.BuildOptions{
281+
cc.balancerWrapper = newCCBalancerWrapper(cc, balancer.BuildOptions{
282282
DialCreds: credsClone,
283283
CredsBundle: cc.dopts.copts.CredsBundle,
284284
Dialer: cc.dopts.copts.Dialer,
285285
Authority: cc.authority,
286286
CustomUserAgent: cc.dopts.copts.UserAgent,
287287
ChannelzParentID: cc.channelzID,
288288
Target: cc.parsedTarget,
289-
}
289+
})
290290

291291
// Build the resolver.
292292
rWrapper, err := newCCResolverWrapper(cc, resolverBuilder)
@@ -465,12 +465,12 @@ type ClientConn struct {
465465
cancel context.CancelFunc // Cancelled on close.
466466

467467
// The following are initialized at dial time, and are read-only after that.
468-
target string // User's dial target.
469-
parsedTarget resolver.Target // See parseTargetAndFindResolver().
470-
authority string // See determineAuthority().
471-
dopts dialOptions // Default and user specified dial options.
472-
balancerBuildOpts balancer.BuildOptions // TODO: delete once we move to the gracefulswitch balancer.
473-
channelzID *channelz.Identifier // Channelz identifier for the channel.
468+
target string // User's dial target.
469+
parsedTarget resolver.Target // See parseTargetAndFindResolver().
470+
authority string // See determineAuthority().
471+
dopts dialOptions // Default and user specified dial options.
472+
channelzID *channelz.Identifier // Channelz identifier for the channel.
473+
balancerWrapper *ccBalancerWrapper // Uses gracefulswitch.balancer underneath.
474474

475475
// The following provide their own synchronization, and therefore don't
476476
// require cc.mu to be held to access them.
@@ -491,8 +491,6 @@ type ClientConn struct {
491491
sc *ServiceConfig // Latest service config received from the resolver.
492492
conns map[*addrConn]struct{} // Set to nil on close.
493493
mkp keepalive.ClientParameters // May be updated upon receipt of a GoAway.
494-
curBalancerName string // TODO: delete as part of https://github.com/grpc/grpc-go/issues/5229.
495-
balancerWrapper *ccBalancerWrapper // TODO: Use gracefulswitch balancer to be able to initialize this once and never rewrite.
496494

497495
lceMu sync.Mutex // protects lastConnectionError
498496
lastConnectionError error
@@ -537,14 +535,7 @@ func (cc *ClientConn) GetState() connectivity.State {
537535
// Notice: This API is EXPERIMENTAL and may be changed or removed in a later
538536
// release.
539537
func (cc *ClientConn) Connect() {
540-
cc.mu.Lock()
541-
defer cc.mu.Unlock()
542-
if cc.balancerWrapper.exitIdle() {
543-
return
544-
}
545-
for ac := range cc.conns {
546-
go ac.connect()
547-
}
538+
cc.balancerWrapper.exitIdle()
548539
}
549540

550541
func (cc *ClientConn) scWatcher() {
@@ -666,21 +657,9 @@ func (cc *ClientConn) updateResolverState(s resolver.State, err error) error {
666657
if cc.sc != nil && cc.sc.lbConfig != nil {
667658
balCfg = cc.sc.lbConfig.cfg
668659
}
669-
670-
cbn := cc.curBalancerName
671660
bw := cc.balancerWrapper
672661
cc.mu.Unlock()
673-
if cbn != grpclbName {
674-
// Filter any grpclb addresses since we don't have the grpclb balancer.
675-
var addrs []resolver.Address
676-
for _, addr := range s.Addresses {
677-
if addr.Type == resolver.GRPCLB {
678-
continue
679-
}
680-
addrs = append(addrs, addr)
681-
}
682-
s.Addresses = addrs
683-
}
662+
684663
uccsErr := bw.updateClientConnState(&balancer.ClientConnState{ResolverState: s, BalancerConfig: balCfg})
685664
if ret == nil {
686665
ret = uccsErr // prefer ErrBadResolver state since any other error is
@@ -709,50 +688,8 @@ func (cc *ClientConn) applyFailingLB(sc *serviceconfig.ParseResult) {
709688
cc.csMgr.updateState(connectivity.TransientFailure)
710689
}
711690

712-
// switchBalancer starts the switching from current balancer to the balancer
713-
// with the given name.
714-
//
715-
// It will NOT send the current address list to the new balancer. If needed,
716-
// caller of this function should send address list to the new balancer after
717-
// this function returns.
718-
//
719-
// Caller must hold cc.mu.
720-
func (cc *ClientConn) switchBalancer(name string) {
721-
if strings.EqualFold(cc.curBalancerName, name) {
722-
return
723-
}
724-
725-
channelz.Infof(logger, cc.channelzID, "ClientConn switching balancer to %q", name)
726-
// Don't hold cc.mu while closing the balancers. The balancers may call
727-
// methods that require cc.mu (e.g. cc.NewSubConn()). Holding the mutex
728-
// would cause a deadlock in that case.
729-
cc.mu.Unlock()
730-
cc.balancerWrapper.close()
731-
cc.mu.Lock()
732-
733-
builder := balancer.Get(name)
734-
if builder == nil {
735-
channelz.Warningf(logger, cc.channelzID, "Channel switches to new LB policy %q due to fallback from invalid balancer name", PickFirstBalancerName)
736-
channelz.Infof(logger, cc.channelzID, "failed to get balancer builder for: %v, using pick_first instead", name)
737-
builder = newPickfirstBuilder()
738-
} else {
739-
channelz.Infof(logger, cc.channelzID, "Channel switches to new LB policy %q", name)
740-
}
741-
742-
cc.curBalancerName = builder.Name()
743-
cc.balancerWrapper = newCCBalancerWrapper(cc, builder, cc.balancerBuildOpts)
744-
}
745-
746691
func (cc *ClientConn) handleSubConnStateChange(sc balancer.SubConn, s connectivity.State, err error) {
747-
cc.mu.Lock()
748-
if cc.conns == nil {
749-
cc.mu.Unlock()
750-
return
751-
}
752-
// TODO(bar switching) send updates to all balancer wrappers when balancer
753-
// gracefully switching is supported.
754-
cc.balancerWrapper.handleSubConnStateChange(sc, s, err)
755-
cc.mu.Unlock()
692+
cc.balancerWrapper.updateSubConnState(sc, s, err)
756693
}
757694

758695
// newAddrConn creates an addrConn for addrs and adds it to cc.conns.
@@ -1002,8 +939,6 @@ func (cc *ClientConn) applyServiceConfigAndBalancer(sc *ServiceConfig, configSel
1002939
cc.retryThrottler.Store((*retryThrottler)(nil))
1003940
}
1004941

1005-
// Only look at balancer types and switch balancer if balancer dial
1006-
// option is not set.
1007942
var newBalancerName string
1008943
if cc.sc != nil && cc.sc.lbConfig != nil {
1009944
newBalancerName = cc.sc.lbConfig.name
@@ -1023,7 +958,7 @@ func (cc *ClientConn) applyServiceConfigAndBalancer(sc *ServiceConfig, configSel
1023958
newBalancerName = PickFirstBalancerName
1024959
}
1025960
}
1026-
cc.switchBalancer(newBalancerName)
961+
cc.balancerWrapper.switchTo(newBalancerName)
1027962
}
1028963

1029964
func (cc *ClientConn) resolveNow(o resolver.ResolveNowOptions) {
@@ -1074,11 +1009,11 @@ func (cc *ClientConn) Close() error {
10741009
rWrapper := cc.resolverWrapper
10751010
cc.resolverWrapper = nil
10761011
bWrapper := cc.balancerWrapper
1077-
cc.balancerWrapper = nil
10781012
cc.mu.Unlock()
10791013

1014+
// The order of closing matters here since the balancer wrapper assumes the
1015+
// picker is closed before it is closed.
10801016
cc.blockingpicker.close()
1081-
10821017
if bWrapper != nil {
10831018
bWrapper.close()
10841019
}

clientconn_test.go

+7-3
Original file line numberDiff line numberDiff line change
@@ -845,9 +845,13 @@ func (s) TestBackoffCancel(t *testing.T) {
845845
if err != nil {
846846
t.Fatalf("Failed to create ClientConn: %v", err)
847847
}
848-
<-dialStrCh
849-
cc.Close()
850-
// Should not leak. May need -count 5000 to exercise.
848+
defer cc.Close()
849+
850+
select {
851+
case <-time.After(defaultTestTimeout):
852+
t.Fatal("Timeout when waiting for custom dialer to be invoked during Dial")
853+
case <-dialStrCh:
854+
}
851855
}
852856

853857
// UpdateAddresses should cause the next reconnect to begin from the top of the

internal/balancer/gracefulswitch/gracefulswitch.go

+7
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,9 @@ func (gsb *Balancer) ResolverError(err error) {
178178
}
179179

180180
// ExitIdle forwards the call to the latest balancer created.
181+
//
182+
// If the latest balancer does not support ExitIdle, the subConns are
183+
// re-connected to manually.
181184
func (gsb *Balancer) ExitIdle() {
182185
balToUpdate := gsb.latestBalancer()
183186
if balToUpdate == nil {
@@ -188,6 +191,10 @@ func (gsb *Balancer) ExitIdle() {
188191
// called.
189192
if ei, ok := balToUpdate.Balancer.(balancer.ExitIdler); ok {
190193
ei.ExitIdle()
194+
return
195+
}
196+
for sc := range balToUpdate.subconns {
197+
sc.Connect()
191198
}
192199
}
193200

internal/balancer/stub/stub.go

+15-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,12 @@
1919
// Package stub implements a balancer for testing purposes.
2020
package stub
2121

22-
import "google.golang.org/grpc/balancer"
22+
import (
23+
"encoding/json"
24+
25+
"google.golang.org/grpc/balancer"
26+
"google.golang.org/grpc/serviceconfig"
27+
)
2328

2429
// BalancerFuncs contains all balancer.Balancer functions with a preceding
2530
// *BalancerData parameter for passing additional instance information. Any
@@ -28,6 +33,8 @@ type BalancerFuncs struct {
2833
// Init is called after ClientConn and BuildOptions are set in
2934
// BalancerData. It may be used to initialize BalancerData.Data.
3035
Init func(*BalancerData)
36+
// ParseConfig is used for parsing LB configs, if specified.
37+
ParseConfig func(json.RawMessage) (serviceconfig.LoadBalancingConfig, error)
3138

3239
UpdateClientConnState func(*BalancerData, balancer.ClientConnState) error
3340
ResolverError func(*BalancerData, error)
@@ -97,6 +104,13 @@ func (bb bb) Build(cc balancer.ClientConn, opts balancer.BuildOptions) balancer.
97104

98105
func (bb bb) Name() string { return bb.name }
99106

107+
func (bb bb) ParseConfig(lbCfg json.RawMessage) (serviceconfig.LoadBalancingConfig, error) {
108+
if bb.bf.ParseConfig != nil {
109+
return bb.bf.ParseConfig(lbCfg)
110+
}
111+
return nil, nil
112+
}
113+
100114
// Register registers a stub balancer builder which will call the provided
101115
// functions. The name used should be unique.
102116
func Register(name string, bf BalancerFuncs) {

0 commit comments

Comments
 (0)