19
19
package priority
20
20
21
21
import (
22
+ "time"
23
+
22
24
"google.golang.org/grpc/balancer"
23
25
"google.golang.org/grpc/balancer/base"
24
26
"google.golang.org/grpc/connectivity"
@@ -36,7 +38,16 @@ type childBalancer struct {
36
38
rState resolver.State
37
39
38
40
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
40
51
}
41
52
42
53
// newChildBalancer creates a child balancer place holder, but doesn't
@@ -79,6 +90,7 @@ func (cb *childBalancer) start() {
79
90
}
80
91
cb .started = true
81
92
cb .parent .bg .Add (cb .name , cb .bb )
93
+ cb .startInitTimer ()
82
94
}
83
95
84
96
// sendUpdate sends the addresses and config to the child balancer.
@@ -103,10 +115,46 @@ func (cb *childBalancer) stop() {
103
115
if ! cb .started {
104
116
return
105
117
}
118
+ cb .stopInitTimer ()
106
119
cb .parent .bg .Remove (cb .name )
107
120
cb .started = false
108
121
cb .state = balancer.State {
109
122
ConnectivityState : connectivity .Connecting ,
110
123
Picker : base .NewErrPicker (balancer .ErrNoSubConnAvailable ),
111
124
}
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 ()
112
160
}
0 commit comments