Skip to content

Commit 08ebd15

Browse files
committed
xds: Use the connected address for locality (grpc#7357)
1 parent c9caa9e commit 08ebd15

6 files changed

Lines changed: 90 additions & 33 deletions

File tree

balancer/balancer.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,8 +72,20 @@ func unregisterForTesting(name string) {
7272
delete(m, name)
7373
}
7474

75+
// getConnectedAddress returns the connected address for a SubConnState.
76+
func getConnectedAddress(scs SubConnState) (resolver.Address, bool) {
77+
return scs.connectedAddress, scs.ConnectivityState == connectivity.Ready
78+
}
79+
80+
// setConnectedAddress sets the connected address for a SubConnState.
81+
func setConnectedAddress(scs *SubConnState, addr resolver.Address) {
82+
scs.connectedAddress = addr
83+
}
84+
7585
func init() {
7686
internal.BalancerUnregister = unregisterForTesting
87+
internal.GetConnectedAddress = getConnectedAddress
88+
internal.SetConnectedAddress = setConnectedAddress
7789
}
7890

7991
// Get returns the resolver builder registered with the given name.
@@ -410,6 +422,9 @@ type SubConnState struct {
410422
// ConnectionError is set if the ConnectivityState is TransientFailure,
411423
// describing the reason the SubConn failed. Otherwise, it is nil.
412424
ConnectionError error
425+
// connectedAddr contains the connected address when ConnectivityState is Ready. Otherwise, it is
426+
// indeterminate.
427+
connectedAddress resolver.Address
413428
}
414429

415430
// ClientConnState describes the state of a ClientConn relevant to the

balancer_wrapper.go

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import (
2525

2626
"google.golang.org/grpc/balancer"
2727
"google.golang.org/grpc/connectivity"
28+
grpcinternal "google.golang.org/grpc/internal"
2829
"google.golang.org/grpc/internal/balancer/gracefulswitch"
2930
"google.golang.org/grpc/internal/channelz"
3031
"google.golang.org/grpc/internal/grpcsync"
@@ -252,15 +253,21 @@ type acBalancerWrapper struct {
252253

253254
// updateState is invoked by grpc to push a subConn state update to the
254255
// underlying balancer.
255-
func (acbw *acBalancerWrapper) updateState(s connectivity.State, err error) {
256+
func (acbw *acBalancerWrapper) updateState(s connectivity.State, curAddr resolver.Address, err error) {
256257
acbw.ccb.serializer.Schedule(func(ctx context.Context) {
257258
if ctx.Err() != nil || acbw.ccb.balancer == nil {
258259
return
259260
}
260261
// Even though it is optional for balancers, gracefulswitch ensures
261262
// opts.StateListener is set, so this cannot ever be nil.
262263
// TODO: delete this comment when UpdateSubConnState is removed.
263-
acbw.stateListener(balancer.SubConnState{ConnectivityState: s, ConnectionError: err})
264+
scs := balancer.SubConnState{ConnectivityState: s, ConnectionError: err}
265+
if s == connectivity.Ready {
266+
if SetConnectedAddress, ok := grpcinternal.SetConnectedAddress.(func(state *balancer.SubConnState, addr resolver.Address)); ok {
267+
SetConnectedAddress(&scs, curAddr)
268+
}
269+
}
270+
acbw.stateListener(scs)
264271
})
265272
}
266273

clientconn.go

Lines changed: 39 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,9 @@ import (
5353

5454
const (
5555
// minimum time to give a connection to complete
56-
minConnectTimeout = 20 * time.Second
56+
minConnectTimeout = 20 * time.Second
57+
withBalancerAttributes = true
58+
withoutBalancerAttributes = false
5759
)
5860

5961
var (
@@ -812,16 +814,26 @@ func (cc *ClientConn) applyFailingLBLocked(sc *serviceconfig.ParseResult) {
812814
cc.csMgr.updateState(connectivity.TransientFailure)
813815
}
814816

815-
// Makes a copy of the input addresses slice and clears out the balancer
816-
// attributes field. Addresses are passed during subconn creation and address
817-
// update operations. In both cases, we will clear the balancer attributes by
818-
// calling this function, and therefore we will be able to use the Equal method
819-
// provided by the resolver.Address type for comparison.
820-
func copyAddressesWithoutBalancerAttributes(in []resolver.Address) []resolver.Address {
817+
// addressWithoutBalancerAttributes returns a copy of the input address with
818+
// the BalancerAttributes field cleared.
819+
func addressWithoutBalancerAttributes(a resolver.Address) resolver.Address {
820+
a.BalancerAttributes = nil
821+
return a
822+
}
823+
824+
// Makes a copy of the input addresses slice and optionally clears out the
825+
// balancer attributes field. Addresses are passed during subconn creation and
826+
// address update operations. In both cases, we may clear the balancer
827+
// attributes by calling this function, which would therefore allow us to use
828+
// the Equal method provided by the resolver.Address type for comparison.
829+
func copyAddresses(in []resolver.Address, includeBalancerAttributes bool) []resolver.Address {
821830
out := make([]resolver.Address, len(in))
822831
for i := range in {
823-
out[i] = in[i]
824-
out[i].BalancerAttributes = nil
832+
if includeBalancerAttributes {
833+
out[i] = in[i]
834+
} else {
835+
out[i] = addressWithoutBalancerAttributes(in[i])
836+
}
825837
}
826838
return out
827839
}
@@ -837,7 +849,7 @@ func (cc *ClientConn) newAddrConnLocked(addrs []resolver.Address, opts balancer.
837849
ac := &addrConn{
838850
state: connectivity.Idle,
839851
cc: cc,
840-
addrs: copyAddressesWithoutBalancerAttributes(addrs),
852+
addrs: copyAddresses(addrs, withBalancerAttributes),
841853
scopts: opts,
842854
dopts: cc.dopts,
843855
channelz: channelz.RegisterSubChannel(cc.channelz, ""),
@@ -924,12 +936,18 @@ func (ac *addrConn) connect() error {
924936
return nil
925937
}
926938

927-
func equalAddresses(a, b []resolver.Address) bool {
939+
func equalAddressIgnoreBalancerAttributes(a, b resolver.Address) bool {
940+
return a.Addr == b.Addr && a.ServerName == b.ServerName &&
941+
a.Attributes.Equal(b.Attributes) &&
942+
a.Metadata == b.Metadata
943+
}
944+
945+
func equalAddressesIgnoreBalancerAttributes(a, b []resolver.Address) bool {
928946
if len(a) != len(b) {
929947
return false
930948
}
931949
for i, v := range a {
932-
if !v.Equal(b[i]) {
950+
if !equalAddressIgnoreBalancerAttributes(v, b[i]) {
933951
return false
934952
}
935953
}
@@ -939,15 +957,15 @@ func equalAddresses(a, b []resolver.Address) bool {
939957
// updateAddrs updates ac.addrs with the new addresses list and handles active
940958
// connections or connection attempts.
941959
func (ac *addrConn) updateAddrs(addrs []resolver.Address) {
942-
addrs = copyAddressesWithoutBalancerAttributes(addrs)
960+
addrs = copyAddresses(addrs, withBalancerAttributes)
943961
limit := len(addrs)
944962
if limit > 5 {
945963
limit = 5
946964
}
947-
channelz.Infof(logger, ac.channelz, "addrConn: updateAddrs addrs (%d of %d): %v", limit, len(addrs), addrs[:limit])
965+
channelz.Infof(logger, ac.channelz, "addrConn: updateAddrs addrs (%d of %d): %v", limit, len(addrs), copyAddresses(addrs[:limit], withoutBalancerAttributes))
948966

949967
ac.mu.Lock()
950-
if equalAddresses(ac.addrs, addrs) {
968+
if equalAddressesIgnoreBalancerAttributes(ac.addrs, addrs) {
951969
ac.mu.Unlock()
952970
return
953971
}
@@ -966,7 +984,7 @@ func (ac *addrConn) updateAddrs(addrs []resolver.Address) {
966984
// Try to find the connected address.
967985
for _, a := range addrs {
968986
a.ServerName = ac.cc.getServerName(a)
969-
if a.Equal(ac.curAddr) {
987+
if equalAddressIgnoreBalancerAttributes(a, ac.curAddr) {
970988
// We are connected to a valid address, so do nothing but
971989
// update the addresses.
972990
ac.mu.Unlock()
@@ -1214,7 +1232,7 @@ func (ac *addrConn) updateConnectivityState(s connectivity.State, lastErr error)
12141232
} else {
12151233
channelz.Infof(logger, ac.channelz, "Subchannel Connectivity change to %v, last error: %s", s, lastErr)
12161234
}
1217-
ac.acbw.updateState(s, lastErr)
1235+
ac.acbw.updateState(s, ac.curAddr, lastErr)
12181236
}
12191237

12201238
// adjustParams updates parameters used to create transports upon
@@ -1347,6 +1365,7 @@ func (ac *addrConn) tryAllAddrs(ctx context.Context, addrs []resolver.Address, c
13471365
// new transport.
13481366
func (ac *addrConn) createTransport(ctx context.Context, addr resolver.Address, copts transport.ConnectOptions, connectDeadline time.Time) error {
13491367
addr.ServerName = ac.cc.getServerName(addr)
1368+
addrWithoutBalancerAttributes := addressWithoutBalancerAttributes(addr)
13501369
hctx, hcancel := context.WithCancel(ctx)
13511370

13521371
onClose := func(r transport.GoAwayReason) {
@@ -1381,14 +1400,14 @@ func (ac *addrConn) createTransport(ctx context.Context, addr resolver.Address,
13811400
defer cancel()
13821401
copts.ChannelzParent = ac.channelz
13831402

1384-
newTr, err := transport.NewClientTransport(connectCtx, ac.cc.ctx, addr, copts, onClose)
1403+
newTr, err := transport.NewClientTransport(connectCtx, ac.cc.ctx, addrWithoutBalancerAttributes, copts, onClose)
13851404
if err != nil {
13861405
if logger.V(2) {
1387-
logger.Infof("Creating new client transport to %q: %v", addr, err)
1406+
logger.Infof("Creating new client transport to %q: %v", addrWithoutBalancerAttributes, err)
13881407
}
13891408
// newTr is either nil, or closed.
13901409
hcancel()
1391-
channelz.Warningf(logger, ac.channelz, "grpc: addrConn.createTransport failed to connect to %s. Err: %v", addr, err)
1410+
channelz.Warningf(logger, ac.channelz, "grpc: addrConn.createTransport failed to connect to %s. Err: %v", addrWithoutBalancerAttributes, err)
13921411
return err
13931412
}
13941413

internal/internal.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,13 @@ var (
208208
// ShuffleAddressListForTesting pseudo-randomizes the order of addresses. n
209209
// is the number of elements. swap swaps the elements with indexes i and j.
210210
ShuffleAddressListForTesting any // func(n int, swap func(i, j int))
211+
212+
// GetConnectedAddress returns the connected address for a SubConnState and
213+
// whether the address is valid based on the state.
214+
GetConnectedAddress any // func (scs SubConnState) (resolver.Address, bool)
215+
216+
// SetConnectedAddress sets the connected address for a SubConnState.
217+
SetConnectedAddress any // func(scs *SubConnState, addr resolver.Address)
211218
)
212219

213220
// HealthChecker defines the signature of the client-side LB channel health

xds/internal/balancer/clusterimpl/clusterimpl.go

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import (
3131

3232
"google.golang.org/grpc/balancer"
3333
"google.golang.org/grpc/connectivity"
34+
grpcinternal "google.golang.org/grpc/internal"
3435
"google.golang.org/grpc/internal/balancer/gracefulswitch"
3536
"google.golang.org/grpc/internal/buffer"
3637
"google.golang.org/grpc/internal/grpclog"
@@ -366,14 +367,27 @@ func (b *clusterImplBalancer) NewSubConn(addrs []resolver.Address, opts balancer
366367
lID = xdsinternal.GetLocalityID(newAddrs[i])
367368
}
368369
var sc balancer.SubConn
370+
ret := &scWrapper{}
369371
oldListener := opts.StateListener
370-
opts.StateListener = func(state balancer.SubConnState) { b.updateSubConnState(sc, state, oldListener) }
372+
opts.StateListener = func(state balancer.SubConnState) {
373+
b.updateSubConnState(sc, state, oldListener)
374+
// Read connected address and call updateLocalityID() based on the connected address's locality.
375+
// https://github.com/grpc/grpc-go/issues/7339
376+
if GetConnectedAddress, ok := grpcinternal.GetConnectedAddress.(func(state balancer.SubConnState) (resolver.Address, bool)); ok {
377+
if addr, ok := GetConnectedAddress(state); ok {
378+
// TODO: Why is lID empty when running the test? The locality info is being lost somehow.
379+
lID := xdsinternal.GetLocalityID(addr)
380+
if !lID.Equal(xdsinternal.LocalityID{}) {
381+
ret.updateLocalityID(lID)
382+
}
383+
}
384+
}
385+
}
371386
sc, err := b.ClientConn.NewSubConn(newAddrs, opts)
372387
if err != nil {
373388
return nil, err
374389
}
375-
// Wrap this SubConn in a wrapper, and add it to the map.
376-
ret := &scWrapper{SubConn: sc}
390+
ret.SubConn = sc
377391
ret.updateLocalityID(lID)
378392
return ret, nil
379393
}

xds/internal/balancer/clusterimpl/tests/balancer_test.go

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -310,14 +310,9 @@ func (s) TestLoadReportingPickFirstMultiLocality(t *testing.T) {
310310
}
311311
mgmtServer.LRSServer.LRSResponseChan <- &resp
312312

313-
// Wait for load to be reported for locality of server 2.
314-
// We (incorrectly) wait for load report for region-2 because presently
315-
// pickfirst always reports load for the locality of the last address in the
316-
// subconn. This will be fixed by ensuring there is only one address per
317-
// subconn.
318-
// TODO(#7339): Change region to region-1 once fixed.
319-
if err := waitForSuccessfulLoadReport(ctx, mgmtServer.LRSServer, "region-2"); err != nil {
320-
t.Fatalf("region-2 did not receive load due to error: %v", err)
313+
// Wait for load to be reported for locality of server 1.
314+
if err := waitForSuccessfulLoadReport(ctx, mgmtServer.LRSServer, "region-1"); err != nil {
315+
t.Fatalf("Server 1 did not receive load due to error: %v", err)
321316
}
322317

323318
// Stop server 1 and send one more rpc. Now the request should go to server 2.

0 commit comments

Comments
 (0)