@@ -39,7 +39,8 @@ const (
39
39
40
40
// jitterFraction is a the limit to the amount of jitter we apply
41
41
// to a user specified MaxQueryTime. We divide the specified time by
42
- // the fraction. So 16 == 6.25% limit of jitter
42
+ // the fraction. So 16 == 6.25% limit of jitter. This jitter is also
43
+ // applied to RPCHoldTimeout.
43
44
jitterFraction = 16
44
45
45
46
// Warn if the Raft command is larger than this.
@@ -175,6 +176,8 @@ func (s *Server) handleNomadConn(conn net.Conn) {
175
176
// forward is used to forward to a remote region or to forward to the local leader
176
177
// Returns a bool of if forwarding was performed, as well as any error
177
178
func (s * Server ) forward (method string , info structs.RPCInfo , args interface {}, reply interface {}) (bool , error ) {
179
+ var firstCheck time.Time
180
+
178
181
region := info .RequestRegion ()
179
182
if region == "" {
180
183
return true , fmt .Errorf ("missing target RPC" )
@@ -191,27 +194,64 @@ func (s *Server) forward(method string, info structs.RPCInfo, args interface{},
191
194
return false , nil
192
195
}
193
196
194
- // Handle leader forwarding
195
- if ! s .IsLeader () {
196
- err := s .forwardLeader (method , args , reply )
197
+ CHECK_LEADER:
198
+ // Find the leader
199
+ isLeader , remoteServer := s .getLeader ()
200
+
201
+ // Handle the case we are the leader
202
+ if isLeader {
203
+ return false , nil
204
+ }
205
+
206
+ // Handle the case of a known leader
207
+ if remoteServer != nil {
208
+ err := s .forwardLeader (remoteServer , method , args , reply )
197
209
return true , err
198
210
}
199
- return false , nil
211
+
212
+ // Gate the request until there is a leader
213
+ if firstCheck .IsZero () {
214
+ firstCheck = time .Now ()
215
+ }
216
+ if time .Now ().Sub (firstCheck ) < s .config .RPCHoldTimeout {
217
+ jitter := lib .RandomStagger (s .config .RPCHoldTimeout / jitterFraction )
218
+ select {
219
+ case <- time .After (jitter ):
220
+ goto CHECK_LEADER
221
+ case <- s .shutdownCh :
222
+ }
223
+ }
224
+
225
+ // No leader found and hold time exceeded
226
+ return true , structs .ErrNoLeader
200
227
}
201
228
202
- // forwardLeader is used to forward an RPC call to the leader, or fail if no leader
203
- func (s * Server ) forwardLeader (method string , args interface {}, reply interface {}) error {
229
+ // getLeader returns if the current node is the leader, and if not
230
+ // then it returns the leader which is potentially nil if the cluster
231
+ // has not yet elected a leader.
232
+ func (s * Server ) getLeader () (bool , * serverParts ) {
233
+ // Check if we are the leader
234
+ if s .IsLeader () {
235
+ return true , nil
236
+ }
237
+
204
238
// Get the leader
205
239
leader := s .raft .Leader ()
206
240
if leader == "" {
207
- return structs . ErrNoLeader
241
+ return false , nil
208
242
}
209
243
210
244
// Lookup the server
211
245
s .peerLock .RLock ()
212
246
server := s .localPeers [leader ]
213
247
s .peerLock .RUnlock ()
214
248
249
+ // Server could be nil
250
+ return false , server
251
+ }
252
+
253
+ // forwardLeader is used to forward an RPC call to the leader, or fail if no leader
254
+ func (s * Server ) forwardLeader (server * serverParts , method string , args interface {}, reply interface {}) error {
215
255
// Handle a missing server
216
256
if server == nil {
217
257
return structs .ErrNoLeader
0 commit comments