@@ -145,6 +145,10 @@ func (c *campaign) manageWin(ctx context.Context, winningEntry *entry.RaceEntry,
145
145
defer func () { wg .Add (1 ); go func () { defer wg .Done (); c .c .OnOusting (ctx ) }() }()
146
146
}
147
147
defer electedCancel ()
148
+ // Wait for any outstanding goroutines before running OnElected (in particular, wait for
149
+ // previous OnOusting callbacks to complete.
150
+ wg .Wait ()
151
+
148
152
wg .Add (1 )
149
153
go func (ctx context.Context ) { defer wg .Done (); c .c .OnElected (ctx , & tv ) }(electedCtx )
150
154
finalEntry , refreshErr := c .refreshLock (ctx , winningEntry , & tv )
@@ -224,14 +228,27 @@ func (c *campaign) refreshLock(ctx context.Context, electedEntry *entry.RaceEntr
224
228
entry := & entryVal
225
229
// we have the lock, we should try to keep it
226
230
for {
231
+ // Check whether we still have some time left on the term before we try to do any
232
+ // real work (if we're past the end of the term, we've lost and need to reacquire
233
+ // anyway)
234
+ if c .clock .Until (entry .TermExpiry ) < 0 {
235
+ return entry , context .DeadlineExceeded
236
+ }
227
237
newEntry , refreshErr := c .refreshLockOnce (ctx , entry )
228
238
switch refreshErr {
229
239
case nil :
230
240
tv .Set (newEntry .TermExpiry )
231
241
entry = newEntry
232
- case context .DeadlineExceeded , context . Canceled , errAcquireFailed :
242
+ case context .Canceled , errAcquireFailed :
233
243
return entry , refreshErr
234
244
default :
245
+ if errors .Is (refreshErr , context .Canceled ) {
246
+ return entry , context .Canceled
247
+ }
248
+ // Check whether we still have some time left on the term before we try to sleep
249
+ if c .clock .Until (entry .TermExpiry ) < 0 {
250
+ return entry , context .DeadlineExceeded
251
+ }
235
252
}
236
253
237
254
// wake up in half the interval until the leadership term
0 commit comments