Skip to content

Commit 175c84c

Browse files
authored
xds/ringhash: use StateListener instead of UpdateSubConnState (#6522)
1 parent 3fa17cc commit 175c84c

File tree

2 files changed

+63
-50
lines changed

2 files changed

+63
-50
lines changed

xds/internal/balancer/ringhash/ringhash.go

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,12 @@ func (b *ringhashBalancer) updateAddresses(addrs []resolver.Address) bool {
218218
addrsSet.Set(addr, true)
219219
newWeight := getWeightAttribute(addr)
220220
if val, ok := b.subConns.Get(addr); !ok {
221-
sc, err := b.cc.NewSubConn([]resolver.Address{addr}, balancer.NewSubConnOptions{HealthCheckEnabled: true})
221+
var sc balancer.SubConn
222+
opts := balancer.NewSubConnOptions{
223+
HealthCheckEnabled: true,
224+
StateListener: func(state balancer.SubConnState) { b.updateSubConnState(sc, state) },
225+
}
226+
sc, err := b.cc.NewSubConn([]resolver.Address{addr}, opts)
222227
if err != nil {
223228
b.logger.Warningf("Failed to create new SubConn: %v", err)
224229
continue
@@ -256,7 +261,7 @@ func (b *ringhashBalancer) updateAddresses(addrs []resolver.Address) bool {
256261
b.subConns.Delete(addr)
257262
addrsUpdated = true
258263
// Keep the state of this sc in b.scStates until sc's state becomes Shutdown.
259-
// The entry will be deleted in UpdateSubConnState.
264+
// The entry will be deleted in updateSubConnState.
260265
}
261266
}
262267
return addrsUpdated
@@ -321,7 +326,11 @@ func (b *ringhashBalancer) ResolverError(err error) {
321326
})
322327
}
323328

324-
// UpdateSubConnState updates the per-SubConn state stored in the ring, and also
329+
func (b *ringhashBalancer) UpdateSubConnState(sc balancer.SubConn, state balancer.SubConnState) {
330+
b.logger.Errorf("UpdateSubConnState(%v, %+v) called unexpectedly", sc, state)
331+
}
332+
333+
// updateSubConnState updates the per-SubConn state stored in the ring, and also
325334
// the aggregated state.
326335
//
327336
// It triggers an update to cc when:
@@ -332,7 +341,7 @@ func (b *ringhashBalancer) ResolverError(err error) {
332341
// - the aggregated state is changed
333342
// - the same picker will be sent again, but this update may trigger a re-pick
334343
// for some RPCs.
335-
func (b *ringhashBalancer) UpdateSubConnState(sc balancer.SubConn, state balancer.SubConnState) {
344+
func (b *ringhashBalancer) updateSubConnState(sc balancer.SubConn, state balancer.SubConnState) {
336345
s := state.ConnectivityState
337346
if logger.V(2) {
338347
b.logger.Infof("Handle SubConn state change: %p, %v", sc, s)

xds/internal/balancer/ringhash/ringhash_test.go

Lines changed: 50 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ func (s) TestUpdateClientConnState_NewRingSize(t *testing.T) {
145145

146146
func (s) TestOneSubConn(t *testing.T) {
147147
wantAddr1 := resolver.Address{Addr: testBackendAddrStrs[0]}
148-
cc, b, p0 := setupTest(t, []resolver.Address{wantAddr1})
148+
cc, _, p0 := setupTest(t, []resolver.Address{wantAddr1})
149149
ring0 := p0.(*picker).ring
150150

151151
firstHash := ring0.items[0].hash
@@ -156,16 +156,16 @@ func (s) TestOneSubConn(t *testing.T) {
156156
if _, err := p0.Pick(balancer.PickInfo{Ctx: ctxWithHash(testHash)}); err != balancer.ErrNoSubConnAvailable {
157157
t.Fatalf("first pick returned err %v, want %v", err, balancer.ErrNoSubConnAvailable)
158158
}
159-
sc0 := ring0.items[0].sc.sc
159+
sc0 := ring0.items[0].sc.sc.(*testutils.TestSubConn)
160160
select {
161-
case <-sc0.(*testutils.TestSubConn).ConnectCh:
161+
case <-sc0.ConnectCh:
162162
case <-time.After(defaultTestTimeout):
163163
t.Errorf("timeout waiting for Connect() from SubConn %v", sc0)
164164
}
165165

166166
// Send state updates to Ready.
167-
b.UpdateSubConnState(sc0, balancer.SubConnState{ConnectivityState: connectivity.Connecting})
168-
b.UpdateSubConnState(sc0, balancer.SubConnState{ConnectivityState: connectivity.Ready})
167+
sc0.UpdateState(balancer.SubConnState{ConnectivityState: connectivity.Connecting})
168+
sc0.UpdateState(balancer.SubConnState{ConnectivityState: connectivity.Ready})
169169

170170
// Test pick with one backend.
171171
p1 := <-cc.NewPickerCh
@@ -186,7 +186,7 @@ func (s) TestThreeSubConnsAffinity(t *testing.T) {
186186
{Addr: testBackendAddrStrs[1]},
187187
{Addr: testBackendAddrStrs[2]},
188188
}
189-
cc, b, p0 := setupTest(t, wantAddrs)
189+
cc, _, p0 := setupTest(t, wantAddrs)
190190
// This test doesn't update addresses, so this ring will be used by all the
191191
// pickers.
192192
ring0 := p0.(*picker).ring
@@ -200,16 +200,16 @@ func (s) TestThreeSubConnsAffinity(t *testing.T) {
200200
t.Fatalf("first pick returned err %v, want %v", err, balancer.ErrNoSubConnAvailable)
201201
}
202202
// The picked SubConn should be the second in the ring.
203-
sc0 := ring0.items[1].sc.sc
203+
sc0 := ring0.items[1].sc.sc.(*testutils.TestSubConn)
204204
select {
205-
case <-sc0.(*testutils.TestSubConn).ConnectCh:
205+
case <-sc0.ConnectCh:
206206
case <-time.After(defaultTestTimeout):
207207
t.Errorf("timeout waiting for Connect() from SubConn %v", sc0)
208208
}
209209

210210
// Send state updates to Ready.
211-
b.UpdateSubConnState(sc0, balancer.SubConnState{ConnectivityState: connectivity.Connecting})
212-
b.UpdateSubConnState(sc0, balancer.SubConnState{ConnectivityState: connectivity.Ready})
211+
sc0.UpdateState(balancer.SubConnState{ConnectivityState: connectivity.Connecting})
212+
sc0.UpdateState(balancer.SubConnState{ConnectivityState: connectivity.Ready})
213213
p1 := <-cc.NewPickerCh
214214
for i := 0; i < 5; i++ {
215215
gotSCSt, _ := p1.Pick(balancer.PickInfo{Ctx: ctxWithHash(testHash)})
@@ -219,7 +219,7 @@ func (s) TestThreeSubConnsAffinity(t *testing.T) {
219219
}
220220

221221
// Turn down the subConn in use.
222-
b.UpdateSubConnState(sc0, balancer.SubConnState{ConnectivityState: connectivity.TransientFailure})
222+
sc0.UpdateState(balancer.SubConnState{ConnectivityState: connectivity.TransientFailure})
223223
p2 := <-cc.NewPickerCh
224224
// Pick with the same hash should be queued, because the SubConn after the
225225
// first picked is Idle.
@@ -228,16 +228,16 @@ func (s) TestThreeSubConnsAffinity(t *testing.T) {
228228
}
229229

230230
// The third SubConn in the ring should connect.
231-
sc1 := ring0.items[2].sc.sc
231+
sc1 := ring0.items[2].sc.sc.(*testutils.TestSubConn)
232232
select {
233-
case <-sc1.(*testutils.TestSubConn).ConnectCh:
233+
case <-sc1.ConnectCh:
234234
case <-time.After(defaultTestTimeout):
235235
t.Errorf("timeout waiting for Connect() from SubConn %v", sc1)
236236
}
237237

238238
// Send state updates to Ready.
239-
b.UpdateSubConnState(sc1, balancer.SubConnState{ConnectivityState: connectivity.Connecting})
240-
b.UpdateSubConnState(sc1, balancer.SubConnState{ConnectivityState: connectivity.Ready})
239+
sc1.UpdateState(balancer.SubConnState{ConnectivityState: connectivity.Connecting})
240+
sc1.UpdateState(balancer.SubConnState{ConnectivityState: connectivity.Ready})
241241
// New picks should all return this SubConn.
242242
p3 := <-cc.NewPickerCh
243243
for i := 0; i < 5; i++ {
@@ -248,19 +248,19 @@ func (s) TestThreeSubConnsAffinity(t *testing.T) {
248248
}
249249

250250
// Now, after backoff, the first picked SubConn will turn Idle.
251-
b.UpdateSubConnState(sc0, balancer.SubConnState{ConnectivityState: connectivity.Idle})
251+
sc0.UpdateState(balancer.SubConnState{ConnectivityState: connectivity.Idle})
252252
// The picks above should have queued Connect() for the first picked
253253
// SubConn, so this Idle state change will trigger a Connect().
254254
select {
255-
case <-sc0.(*testutils.TestSubConn).ConnectCh:
255+
case <-sc0.ConnectCh:
256256
case <-time.After(defaultTestTimeout):
257257
t.Errorf("timeout waiting for Connect() from SubConn %v", sc0)
258258
}
259259

260260
// After the first picked SubConn turn Ready, new picks should return it
261261
// again (even though the second picked SubConn is also Ready).
262-
b.UpdateSubConnState(sc0, balancer.SubConnState{ConnectivityState: connectivity.Connecting})
263-
b.UpdateSubConnState(sc0, balancer.SubConnState{ConnectivityState: connectivity.Ready})
262+
sc0.UpdateState(balancer.SubConnState{ConnectivityState: connectivity.Connecting})
263+
sc0.UpdateState(balancer.SubConnState{ConnectivityState: connectivity.Ready})
264264
p4 := <-cc.NewPickerCh
265265
for i := 0; i < 5; i++ {
266266
gotSCSt, _ := p4.Pick(balancer.PickInfo{Ctx: ctxWithHash(testHash)})
@@ -279,7 +279,7 @@ func (s) TestThreeSubConnsAffinityMultiple(t *testing.T) {
279279
{Addr: testBackendAddrStrs[1]},
280280
{Addr: testBackendAddrStrs[2]},
281281
}
282-
cc, b, p0 := setupTest(t, wantAddrs)
282+
cc, _, p0 := setupTest(t, wantAddrs)
283283
// This test doesn't update addresses, so this ring will be used by all the
284284
// pickers.
285285
ring0 := p0.(*picker).ring
@@ -292,16 +292,16 @@ func (s) TestThreeSubConnsAffinityMultiple(t *testing.T) {
292292
if _, err := p0.Pick(balancer.PickInfo{Ctx: ctxWithHash(testHash)}); err != balancer.ErrNoSubConnAvailable {
293293
t.Fatalf("first pick returned err %v, want %v", err, balancer.ErrNoSubConnAvailable)
294294
}
295-
sc0 := ring0.items[1].sc.sc
295+
sc0 := ring0.items[1].sc.sc.(*testutils.TestSubConn)
296296
select {
297-
case <-sc0.(*testutils.TestSubConn).ConnectCh:
297+
case <-sc0.ConnectCh:
298298
case <-time.After(defaultTestTimeout):
299299
t.Errorf("timeout waiting for Connect() from SubConn %v", sc0)
300300
}
301301

302302
// Send state updates to Ready.
303-
b.UpdateSubConnState(sc0, balancer.SubConnState{ConnectivityState: connectivity.Connecting})
304-
b.UpdateSubConnState(sc0, balancer.SubConnState{ConnectivityState: connectivity.Ready})
303+
sc0.UpdateState(balancer.SubConnState{ConnectivityState: connectivity.Connecting})
304+
sc0.UpdateState(balancer.SubConnState{ConnectivityState: connectivity.Ready})
305305

306306
// First hash should always pick sc0.
307307
p1 := <-cc.NewPickerCh
@@ -318,14 +318,14 @@ func (s) TestThreeSubConnsAffinityMultiple(t *testing.T) {
318318
if _, err := p0.Pick(balancer.PickInfo{Ctx: ctxWithHash(testHash2)}); err != balancer.ErrNoSubConnAvailable {
319319
t.Fatalf("first pick returned err %v, want %v", err, balancer.ErrNoSubConnAvailable)
320320
}
321-
sc1 := ring0.items[2].sc.sc
321+
sc1 := ring0.items[2].sc.sc.(*testutils.TestSubConn)
322322
select {
323-
case <-sc1.(*testutils.TestSubConn).ConnectCh:
323+
case <-sc1.ConnectCh:
324324
case <-time.After(defaultTestTimeout):
325325
t.Errorf("timeout waiting for Connect() from SubConn %v", sc1)
326326
}
327-
b.UpdateSubConnState(sc1, balancer.SubConnState{ConnectivityState: connectivity.Connecting})
328-
b.UpdateSubConnState(sc1, balancer.SubConnState{ConnectivityState: connectivity.Ready})
327+
sc1.UpdateState(balancer.SubConnState{ConnectivityState: connectivity.Connecting})
328+
sc1.UpdateState(balancer.SubConnState{ConnectivityState: connectivity.Ready})
329329

330330
// With the new generated picker, hash2 always picks sc1.
331331
p2 := <-cc.NewPickerCh
@@ -419,58 +419,62 @@ func (s) TestSubConnToConnectWhenOverallTransientFailure(t *testing.T) {
419419
{Addr: testBackendAddrStrs[1]},
420420
{Addr: testBackendAddrStrs[2]},
421421
}
422-
_, b, p0 := setupTest(t, wantAddrs)
422+
_, _, p0 := setupTest(t, wantAddrs)
423423
ring0 := p0.(*picker).ring
424424

425+
// ringhash won't tell SCs to connect until there is an RPC, so simulate
426+
// one now.
427+
p0.Pick(balancer.PickInfo{Ctx: context.Background()})
428+
425429
// Turn the first subconn to transient failure.
426-
sc0 := ring0.items[0].sc.sc
427-
b.UpdateSubConnState(sc0, balancer.SubConnState{ConnectivityState: connectivity.TransientFailure})
428-
b.UpdateSubConnState(sc0, balancer.SubConnState{ConnectivityState: connectivity.Idle})
430+
sc0 := ring0.items[0].sc.sc.(*testutils.TestSubConn)
431+
sc0.UpdateState(balancer.SubConnState{ConnectivityState: connectivity.TransientFailure})
432+
sc0.UpdateState(balancer.SubConnState{ConnectivityState: connectivity.Idle})
429433

430434
// It will trigger the second subconn to connect (because overall state is
431435
// Connect (when one subconn is TF)).
432-
sc1 := ring0.items[1].sc.sc
436+
sc1 := ring0.items[1].sc.sc.(*testutils.TestSubConn)
433437
select {
434-
case <-sc1.(*testutils.TestSubConn).ConnectCh:
438+
case <-sc1.ConnectCh:
435439
case <-time.After(defaultTestShortTimeout):
436440
t.Fatalf("timeout waiting for Connect() from SubConn %v", sc1)
437441
}
438442

439443
// Turn the second subconn to TF. This will set the overall state to TF.
440-
b.UpdateSubConnState(sc1, balancer.SubConnState{ConnectivityState: connectivity.TransientFailure})
441-
b.UpdateSubConnState(sc1, balancer.SubConnState{ConnectivityState: connectivity.Idle})
444+
sc1.UpdateState(balancer.SubConnState{ConnectivityState: connectivity.TransientFailure})
445+
sc1.UpdateState(balancer.SubConnState{ConnectivityState: connectivity.Idle})
442446

443447
// It will trigger the third subconn to connect.
444-
sc2 := ring0.items[2].sc.sc
448+
sc2 := ring0.items[2].sc.sc.(*testutils.TestSubConn)
445449
select {
446-
case <-sc2.(*testutils.TestSubConn).ConnectCh:
450+
case <-sc2.ConnectCh:
447451
case <-time.After(defaultTestShortTimeout):
448452
t.Fatalf("timeout waiting for Connect() from SubConn %v", sc2)
449453
}
450454

451455
// Turn the third subconn to TF. This will set the overall state to TF.
452-
b.UpdateSubConnState(sc2, balancer.SubConnState{ConnectivityState: connectivity.TransientFailure})
453-
b.UpdateSubConnState(sc2, balancer.SubConnState{ConnectivityState: connectivity.Idle})
456+
sc2.UpdateState(balancer.SubConnState{ConnectivityState: connectivity.TransientFailure})
457+
sc2.UpdateState(balancer.SubConnState{ConnectivityState: connectivity.Idle})
454458

455459
// It will trigger the first subconn to connect.
456460
select {
457-
case <-sc0.(*testutils.TestSubConn).ConnectCh:
461+
case <-sc0.ConnectCh:
458462
case <-time.After(defaultTestShortTimeout):
459463
t.Fatalf("timeout waiting for Connect() from SubConn %v", sc0)
460464
}
461465

462466
// Turn the third subconn to TF again.
463-
b.UpdateSubConnState(sc2, balancer.SubConnState{ConnectivityState: connectivity.TransientFailure})
464-
b.UpdateSubConnState(sc2, balancer.SubConnState{ConnectivityState: connectivity.Idle})
467+
sc2.UpdateState(balancer.SubConnState{ConnectivityState: connectivity.TransientFailure})
468+
sc2.UpdateState(balancer.SubConnState{ConnectivityState: connectivity.Idle})
465469

466470
// This will not trigger any new Connect() on the SubConns, because sc0 is
467471
// still attempting to connect, and we only need one SubConn to connect.
468472
select {
469-
case <-sc0.(*testutils.TestSubConn).ConnectCh:
473+
case <-sc0.ConnectCh:
470474
t.Fatalf("unexpected Connect() from SubConn %v", sc0)
471-
case <-sc1.(*testutils.TestSubConn).ConnectCh:
475+
case <-sc1.ConnectCh:
472476
t.Fatalf("unexpected Connect() from SubConn %v", sc1)
473-
case <-sc2.(*testutils.TestSubConn).ConnectCh:
477+
case <-sc2.ConnectCh:
474478
t.Fatalf("unexpected Connect() from SubConn %v", sc2)
475479
case <-time.After(defaultTestShortTimeout):
476480
}

0 commit comments

Comments
 (0)