Skip to content

Commit 80e380e

Browse files
authored
balancer/base: keep address attributes for pickers (#4253)
1 parent 702608f commit 80e380e

File tree

2 files changed

+72
-11
lines changed

2 files changed

+72
-11
lines changed

balancer/base/balancer.go

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
"errors"
2323
"fmt"
2424

25+
"google.golang.org/grpc/attributes"
2526
"google.golang.org/grpc/balancer"
2627
"google.golang.org/grpc/connectivity"
2728
"google.golang.org/grpc/grpclog"
@@ -41,7 +42,7 @@ func (bb *baseBuilder) Build(cc balancer.ClientConn, opt balancer.BuildOptions)
4142
cc: cc,
4243
pickerBuilder: bb.pickerBuilder,
4344

44-
subConns: make(map[resolver.Address]balancer.SubConn),
45+
subConns: make(map[resolver.Address]subConnInfo),
4546
scStates: make(map[balancer.SubConn]connectivity.State),
4647
csEvltr: &balancer.ConnectivityStateEvaluator{},
4748
config: bb.config,
@@ -57,14 +58,19 @@ func (bb *baseBuilder) Name() string {
5758
return bb.name
5859
}
5960

61+
type subConnInfo struct {
62+
subConn balancer.SubConn
63+
attrs *attributes.Attributes
64+
}
65+
6066
type baseBalancer struct {
6167
cc balancer.ClientConn
6268
pickerBuilder PickerBuilder
6369

6470
csEvltr *balancer.ConnectivityStateEvaluator
6571
state connectivity.State
6672

67-
subConns map[resolver.Address]balancer.SubConn // `attributes` is stripped from the keys of this map (the addresses)
73+
subConns map[resolver.Address]subConnInfo // `attributes` is stripped from the keys of this map (the addresses)
6874
scStates map[balancer.SubConn]connectivity.State
6975
picker balancer.Picker
7076
config Config
@@ -114,7 +120,7 @@ func (b *baseBalancer) UpdateClientConnState(s balancer.ClientConnState) error {
114120
aNoAttrs := a
115121
aNoAttrs.Attributes = nil
116122
addrsSet[aNoAttrs] = struct{}{}
117-
if sc, ok := b.subConns[aNoAttrs]; !ok {
123+
if scInfo, ok := b.subConns[aNoAttrs]; !ok {
118124
// a is a new address (not existing in b.subConns).
119125
//
120126
// When creating SubConn, the original address with attributes is
@@ -125,7 +131,7 @@ func (b *baseBalancer) UpdateClientConnState(s balancer.ClientConnState) error {
125131
logger.Warningf("base.baseBalancer: failed to create new SubConn: %v", err)
126132
continue
127133
}
128-
b.subConns[aNoAttrs] = sc
134+
b.subConns[aNoAttrs] = subConnInfo{subConn: sc, attrs: a.Attributes}
129135
b.scStates[sc] = connectivity.Idle
130136
sc.Connect()
131137
} else {
@@ -135,13 +141,15 @@ func (b *baseBalancer) UpdateClientConnState(s balancer.ClientConnState) error {
135141
// The SubConn does a reflect.DeepEqual of the new and old
136142
// addresses. So this is a noop if the current address is the same
137143
// as the old one (including attributes).
138-
b.cc.UpdateAddresses(sc, []resolver.Address{a})
144+
scInfo.attrs = a.Attributes
145+
b.subConns[aNoAttrs] = scInfo
146+
b.cc.UpdateAddresses(scInfo.subConn, []resolver.Address{a})
139147
}
140148
}
141-
for a, sc := range b.subConns {
149+
for a, scInfo := range b.subConns {
142150
// a was removed by resolver.
143151
if _, ok := addrsSet[a]; !ok {
144-
b.cc.RemoveSubConn(sc)
152+
b.cc.RemoveSubConn(scInfo.subConn)
145153
delete(b.subConns, a)
146154
// Keep the state of this sc in b.scStates until sc's state becomes Shutdown.
147155
// The entry will be deleted in UpdateSubConnState.
@@ -184,9 +192,10 @@ func (b *baseBalancer) regeneratePicker() {
184192
readySCs := make(map[balancer.SubConn]SubConnInfo)
185193

186194
// Filter out all ready SCs from full subConn map.
187-
for addr, sc := range b.subConns {
188-
if st, ok := b.scStates[sc]; ok && st == connectivity.Ready {
189-
readySCs[sc] = SubConnInfo{Address: addr}
195+
for addr, scInfo := range b.subConns {
196+
if st, ok := b.scStates[scInfo.subConn]; ok && st == connectivity.Ready {
197+
addr.Attributes = scInfo.attrs
198+
readySCs[scInfo.subConn] = SubConnInfo{Address: addr}
190199
}
191200
}
192201
b.picker = b.pickerBuilder.Build(PickerBuildInfo{ReadySCs: readySCs})

balancer/base/balancer_test.go

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import (
2323

2424
"google.golang.org/grpc/attributes"
2525
"google.golang.org/grpc/balancer"
26+
"google.golang.org/grpc/connectivity"
2627
"google.golang.org/grpc/resolver"
2728
)
2829

@@ -35,12 +36,24 @@ func (c *testClientConn) NewSubConn(addrs []resolver.Address, opts balancer.NewS
3536
return c.newSubConn(addrs, opts)
3637
}
3738

39+
func (c *testClientConn) UpdateState(balancer.State) {}
40+
3841
type testSubConn struct{}
3942

4043
func (sc *testSubConn) UpdateAddresses(addresses []resolver.Address) {}
4144

4245
func (sc *testSubConn) Connect() {}
4346

47+
// testPickBuilder creates balancer.Picker for test.
48+
type testPickBuilder struct {
49+
validate func(info PickerBuildInfo)
50+
}
51+
52+
func (p *testPickBuilder) Build(info PickerBuildInfo) balancer.Picker {
53+
p.validate(info)
54+
return nil
55+
}
56+
4457
func TestBaseBalancerStripAttributes(t *testing.T) {
4558
b := (&baseBuilder{}).Build(&testClientConn{
4659
newSubConn: func(addrs []resolver.Address, _ balancer.NewSubConnOptions) (balancer.SubConn, error) {
@@ -64,7 +77,46 @@ func TestBaseBalancerStripAttributes(t *testing.T) {
6477

6578
for addr := range b.subConns {
6679
if addr.Attributes != nil {
67-
t.Errorf("in b.subConns, got address %+v with nil attributes, want not nil", addr)
80+
t.Errorf("in b.subConns, got address %+v with not nil attributes, want nil", addr)
81+
}
82+
}
83+
}
84+
85+
func TestBaseBalancerReserveAttributes(t *testing.T) {
86+
var v = func(info PickerBuildInfo) {
87+
for _, sc := range info.ReadySCs {
88+
if sc.Address.Addr == "1.1.1.1" {
89+
if sc.Address.Attributes == nil {
90+
t.Errorf("in picker.validate, got address %+v with nil attributes, want not nil", sc.Address)
91+
}
92+
foo, ok := sc.Address.Attributes.Value("foo").(string)
93+
if !ok || foo != "2233niang" {
94+
t.Errorf("in picker.validate, got address[1.1.1.1] with invalid attributes value %v, want 2233niang", sc.Address.Attributes.Value("foo"))
95+
}
96+
} else if sc.Address.Addr == "2.2.2.2" {
97+
if sc.Address.Attributes != nil {
98+
t.Error("in b.subConns, got address[2.2.2.2] with not nil attributes, want nil")
99+
}
100+
}
68101
}
69102
}
103+
pickBuilder := &testPickBuilder{validate: v}
104+
b := (&baseBuilder{pickerBuilder: pickBuilder}).Build(&testClientConn{
105+
newSubConn: func(addrs []resolver.Address, _ balancer.NewSubConnOptions) (balancer.SubConn, error) {
106+
return &testSubConn{}, nil
107+
},
108+
}, balancer.BuildOptions{}).(*baseBalancer)
109+
110+
b.UpdateClientConnState(balancer.ClientConnState{
111+
ResolverState: resolver.State{
112+
Addresses: []resolver.Address{
113+
{Addr: "1.1.1.1", Attributes: attributes.New("foo", "2233niang")},
114+
{Addr: "2.2.2.2", Attributes: nil},
115+
},
116+
},
117+
})
118+
119+
for sc := range b.scStates {
120+
b.UpdateSubConnState(sc, balancer.SubConnState{ConnectivityState: connectivity.Ready, ConnectionError: nil})
121+
}
70122
}

0 commit comments

Comments
 (0)