@@ -10,6 +10,7 @@ import (
10
10
"time"
11
11
12
12
"github.com/armon/go-metrics"
13
+ "github.com/hashicorp/consul/consul/agent"
13
14
"github.com/hashicorp/consul/consul/state"
14
15
"github.com/hashicorp/consul/consul/structs"
15
16
"github.com/hashicorp/consul/lib"
@@ -39,7 +40,8 @@ const (
39
40
40
41
// jitterFraction is a the limit to the amount of jitter we apply
41
42
// to a user specified MaxQueryTime. We divide the specified time by
42
- // the fraction. So 16 == 6.25% limit of jitter
43
+ // the fraction. So 16 == 6.25% limit of jitter. This same fraction
44
+ // is applied to the RPCHoldTimeout
43
45
jitterFraction = 16
44
46
45
47
// Warn if the Raft command is larger than this.
@@ -189,6 +191,8 @@ func (s *Server) handleConsulConn(conn net.Conn) {
189
191
// forward is used to forward to a remote DC or to forward to the local leader
190
192
// Returns a bool of if forwarding was performed, as well as any error
191
193
func (s * Server ) forward (method string , info structs.RPCInfo , args interface {}, reply interface {}) (bool , error ) {
194
+ var firstCheck time.Time
195
+
192
196
// Handle DC forwarding
193
197
dc := info .RequestDatacenter ()
194
198
if dc != s .config .Datacenter {
@@ -201,27 +205,64 @@ func (s *Server) forward(method string, info structs.RPCInfo, args interface{},
201
205
return false , nil
202
206
}
203
207
204
- // Handle leader forwarding
205
- if ! s .IsLeader () {
206
- err := s .forwardLeader (method , args , reply )
208
+ CHECK_LEADER:
209
+ // Find the leader
210
+ isLeader , remoteServer := s .getLeader ()
211
+
212
+ // Handle the case we are the leader
213
+ if isLeader {
214
+ return false , nil
215
+ }
216
+
217
+ // Handle the case of a known leader
218
+ if remoteServer != nil {
219
+ err := s .forwardLeader (remoteServer , method , args , reply )
207
220
return true , err
208
221
}
209
- return false , nil
222
+
223
+ // Gate the request until there is a leader
224
+ if firstCheck .IsZero () {
225
+ firstCheck = time .Now ()
226
+ }
227
+ if time .Now ().Sub (firstCheck ) < s .config .RPCHoldTimeout {
228
+ jitter := lib .RandomStagger (s .config .RPCHoldTimeout / jitterFraction )
229
+ select {
230
+ case <- time .After (jitter ):
231
+ goto CHECK_LEADER
232
+ case <- s .shutdownCh :
233
+ }
234
+ }
235
+
236
+ // No leader found and hold time exceeded
237
+ return true , structs .ErrNoLeader
210
238
}
211
239
212
- // forwardLeader is used to forward an RPC call to the leader, or fail if no leader
213
- func (s * Server ) forwardLeader (method string , args interface {}, reply interface {}) error {
240
+ // getLeader returns if the current node is the leader, and if not
241
+ // then it returns the leader which is potentially nil if the cluster
242
+ // has not yet elected a leader.
243
+ func (s * Server ) getLeader () (bool , * agent.Server ) {
244
+ // Check if we are the leader
245
+ if s .IsLeader () {
246
+ return true , nil
247
+ }
248
+
214
249
// Get the leader
215
250
leader := s .raft .Leader ()
216
251
if leader == "" {
217
- return structs . ErrNoLeader
252
+ return false , nil
218
253
}
219
254
220
255
// Lookup the server
221
256
s .localLock .RLock ()
222
257
server := s .localConsuls [leader ]
223
258
s .localLock .RUnlock ()
224
259
260
+ // Server could be nil
261
+ return false , server
262
+ }
263
+
264
+ // forwardLeader is used to forward an RPC call to the leader, or fail if no leader
265
+ func (s * Server ) forwardLeader (server * agent.Server , method string , args interface {}, reply interface {}) error {
225
266
// Handle a missing server
226
267
if server == nil {
227
268
return structs .ErrNoLeader
0 commit comments