public async Task RunElectionsForever(CancellationToken token) { var leaseWrapper = new BlobLeaseWrapper(_blob, 512); while (!token.IsCancellationRequested) { await GrabLeaseOrWait(leaseWrapper, token); } }
async Task GrabLeaseOrWait(BlobLeaseWrapper leaseWrapper, CancellationToken token) { // Try to acquire the blob lease, otherwise wait for some time before we can try again. var leaseId = await TryAcquireLeaseOrWait(leaseWrapper, token); if (string.IsNullOrEmpty(leaseId)) { return; } // Create a new linked cancellation token source, so if either the // original token is canceled or the lease cannot be renewed, // then the leader task can be canceled. using (var cts = CancellationTokenSource.CreateLinkedTokenSource(new[] {token})) { // Run the leader task. var leaderTask = _leaderTask.Invoke(cts.Token, _blob); // Keeps renewing the lease in regular intervals. // If the lease cannot be renewed, then the task completes. var renew = KeepRenewingLease(leaseWrapper, leaseId, cts.Token); // When any task completes (either the leader task or when it could // not renew the lease) then cancel the other task. await CancelAllWhenAnyCompletes(leaderTask, renew, cts); } }
static async Task<string> TryAcquireLeaseOrWait(BlobLeaseWrapper leaseWrapper, CancellationToken token) { try { var leaseId = await leaseWrapper.AcquireLeaseAsync(token); if (!string.IsNullOrEmpty(leaseId)) { return leaseId; } await Task.Delay(AcquireAttemptInterval, token); return null; } catch (OperationCanceledException) { return null; } }
static async Task KeepRenewingLease(BlobLeaseWrapper leaseWrapper, string leaseId, CancellationToken token) { var renewOffset = new Stopwatch(); while (!token.IsCancellationRequested) { try { // Immediately attempt to renew the lease // We cannot be sure how much time has passed since the lease was actually acquired renewOffset.Restart(); var renewed = await leaseWrapper.RenewLeaseAsync(leaseId, token); renewOffset.Stop(); if (!renewed) { return; } // We delay based on the time from the start of the last renew request to ensure var renewIntervalAdjusted = RenewInterval - renewOffset.Elapsed; // If the adjusted interval is greater than zero wait for that long if (renewIntervalAdjusted > TimeSpan.Zero) { await Task.Delay(RenewInterval - renewOffset.Elapsed, token); } } catch (OperationCanceledException) { leaseWrapper.ReleaseLease(leaseId); return; } } }