Skip to content

Commit db79903

Browse files
authored
xds/priority: start the init timer when a child switch to Connecting from non-transient-failure state (#5334)
1 parent 462d867 commit db79903

File tree

4 files changed

+128
-185
lines changed

4 files changed

+128
-185
lines changed

xds/internal/balancer/priority/balancer.go

Lines changed: 5 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -100,12 +100,6 @@ type priorityBalancer struct {
100100
childToPriority map[string]int
101101
// children is a map from child name to sub-balancers.
102102
children map[string]*childBalancer
103-
// The timer to give a priority some time to connect. And if the priority
104-
// doesn't go into Ready/Failure, the next priority will be started.
105-
//
106-
// One timer is enough because there can be at most one priority in init
107-
// state.
108-
priorityInitTimer *timerWrapper
109103
}
110104

111105
func (b *priorityBalancer) UpdateClientConnState(s balancer.ClientConnState) error {
@@ -198,27 +192,17 @@ func (b *priorityBalancer) Close() {
198192
// Clear states of the current child in use, so if there's a race in picker
199193
// update, it will be dropped.
200194
b.childInUse = ""
201-
b.stopPriorityInitTimer()
195+
// Stop the child policies, this is necessary to stop the init timers in the
196+
// children.
197+
for _, child := range b.children {
198+
child.stop()
199+
}
202200
}
203201

204202
func (b *priorityBalancer) ExitIdle() {
205203
b.bg.ExitIdle()
206204
}
207205

208-
// stopPriorityInitTimer stops the priorityInitTimer if it's not nil, and set it
209-
// to nil.
210-
//
211-
// Caller must hold b.mu.
212-
func (b *priorityBalancer) stopPriorityInitTimer() {
213-
timerW := b.priorityInitTimer
214-
if timerW == nil {
215-
return
216-
}
217-
b.priorityInitTimer = nil
218-
timerW.stopped = true
219-
timerW.timer.Stop()
220-
}
221-
222206
// UpdateState implements balancergroup.BalancerStateAggregator interface. The
223207
// balancer group sends new connectivity state and picker here.
224208
func (b *priorityBalancer) UpdateState(childName string, state balancer.State) {

xds/internal/balancer/priority/balancer_child.go

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
package priority
2020

2121
import (
22+
"time"
23+
2224
"google.golang.org/grpc/balancer"
2325
"google.golang.org/grpc/balancer/base"
2426
"google.golang.org/grpc/connectivity"
@@ -36,7 +38,16 @@ type childBalancer struct {
3638
rState resolver.State
3739

3840
started bool
39-
state balancer.State
41+
// This is set when the child reports TransientFailure, and unset when it
42+
// reports Ready or Idle. It is used to decide whether the failover timer
43+
// should start when the child is transitioning into Connecting. The timer
44+
// will be restarted if the child has not reported TF more recently than it
45+
// reported Ready or Idle.
46+
reportedTF bool
47+
state balancer.State
48+
// The timer to give a priority some time to connect. And if the priority
49+
// doesn't go into Ready/Failure, the next priority will be started.
50+
initTimer *timerWrapper
4051
}
4152

4253
// newChildBalancer creates a child balancer place holder, but doesn't
@@ -79,6 +90,7 @@ func (cb *childBalancer) start() {
7990
}
8091
cb.started = true
8192
cb.parent.bg.Add(cb.name, cb.bb)
93+
cb.startInitTimer()
8294
}
8395

8496
// sendUpdate sends the addresses and config to the child balancer.
@@ -103,10 +115,46 @@ func (cb *childBalancer) stop() {
103115
if !cb.started {
104116
return
105117
}
118+
cb.stopInitTimer()
106119
cb.parent.bg.Remove(cb.name)
107120
cb.started = false
108121
cb.state = balancer.State{
109122
ConnectivityState: connectivity.Connecting,
110123
Picker: base.NewErrPicker(balancer.ErrNoSubConnAvailable),
111124
}
125+
// Clear child.reportedTF, so that if this child is started later, it will
126+
// be given time to connect.
127+
cb.reportedTF = false
128+
}
129+
130+
func (cb *childBalancer) startInitTimer() {
131+
if cb.initTimer != nil {
132+
return
133+
}
134+
// Need this local variable to capture timerW in the AfterFunc closure
135+
// to check the stopped boolean.
136+
timerW := &timerWrapper{}
137+
cb.initTimer = timerW
138+
timerW.timer = time.AfterFunc(DefaultPriorityInitTimeout, func() {
139+
cb.parent.mu.Lock()
140+
defer cb.parent.mu.Unlock()
141+
if timerW.stopped {
142+
return
143+
}
144+
cb.initTimer = nil
145+
// Re-sync the priority. This will switch to the next priority if
146+
// there's any. Note that it's important sync() is called after setting
147+
// initTimer to nil.
148+
cb.parent.syncPriority()
149+
})
150+
}
151+
152+
func (cb *childBalancer) stopInitTimer() {
153+
timerW := cb.initTimer
154+
if timerW == nil {
155+
return
156+
}
157+
cb.initTimer = nil
158+
timerW.stopped = true
159+
timerW.timer.Stop()
112160
}

0 commit comments

Comments
 (0)