/// <summary> /// Tries to acquire the lock indefinitely until the action succeeds. /// </summary> /// <param name="manager">The instance of <see cref="ILockManager"/>.</param> /// <param name="name">Topic or name.</param> /// <param name="retryAfter">Specifies the duration in seconds to wait for a particular retry attempt.</param> /// <param name="cancellationToken">Propagates notification that operations should be canceled.</param> /// <returns>The task that represents the asynchronous operation result for acquiring the lock.</returns> public static async Task <LockLeaseResult> TryAcquireLockForeverPolicy(this ILockManager manager, string name, int retryAfter = 3, CancellationToken cancellationToken = default) { var policy = Policy.HandleResult <LockLeaseResult>(lockLeaseResult => !lockLeaseResult.Ok) .WaitAndRetryForeverAsync(retryAttempt => TimeSpan.FromSeconds(retryAfter)); return(await policy.ExecuteAsync(token => manager.TryAcquireLock(name), cancellationToken)); }
/// <summary> /// Uses a lease on a shared resource imposed by the <see cref="ILockManager"/> (i.e an Azure Storage blob or a shared file in a NFS) to provide a mechanism for implementing a shared, distributed mutex. /// </summary> /// <remarks> /// This mutex can be used to elect a leader among a group of role instances in an Azure cloud service or an on-premise infrastructure. /// The first role instance to acquire the lease is elected the leader, and remains the leader until it releases the lease or isn't able to renew the lease. /// </remarks> /// <param name="manager">The instance of <see cref="ILockManager"/>.</param> /// <param name="task">A task that references the code that the role instance should run if it successfully acquires the lease over the blob and is elected the leader.</param> /// <param name="taskName">A name for the task to run.</param> /// <param name="cancellationToken">Propagates notification that operations should be canceled.</param> /// <param name="options">Options for configuring the ExclusiveRun method.</param> /// <returns>The task that represents the asynchronous operation result for running the specified delegate.</returns> public static async Task ExclusiveRun(this ILockManager manager, string taskName, Func <CancellationToken, Task> task, CancellationToken cancellationToken, ExclusiveRunOptions options) { // Run in a while loop as long as cancellation has not been requested for the provided token. CancellationTokenSource linkedTokenSource = null; while (!(linkedTokenSource?.IsCancellationRequested ?? false)) { // Try to acquire the lock. var lockResult = await manager.TryAcquireLock(taskName, duration : TimeSpan.FromSeconds(options.LockDuration), cancellationToken : cancellationToken); // If it is not possible to acquire the lock, wait for 30 seconds and try again. Being a leader requires patience and consistency :) if (!lockResult.Ok) { if (options.RetryIntervalInSeconds.HasValue) { await Task.Delay(TimeSpan.FromSeconds(options.RetryIntervalInSeconds.Value), cancellationToken); continue; } else { break; } } // 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. linkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); // The role instance that is executing the following code is now the leader. using (lockResult.Lock) { // The leader task. var leaderTask = task.Invoke(linkedTokenSource.Token); // The renew lease task. var renewLeaseTask = TryRenewUntilCancelled(lockResult.Lock, linkedTokenSource.Token, intervalInSeconds: (int)Math.Round((decimal)options.LockDuration * (2 / 3))); // Wait for either of the two tasks to complete. await Task.WhenAny(leaderTask, renewLeaseTask); } // Cancel the user's leader task or the renew lease task, as the current role instance is no longer the leader. linkedTokenSource.Cancel(); } linkedTokenSource?.Dispose(); }
public async Task AquireLockTest() { var duration = TimeSpan.FromSeconds(15); var name = "constantinos"; // using a random name :) var @lock = await _LockManager.AcquireLock(name, duration); await using (@lock) { await Task.Delay(TimeSpan.FromSeconds(0.5)); } var @lock2 = await _LockManager.AcquireLock(name, duration); await using (@lock2) { await Task.Delay(TimeSpan.FromSeconds(0.5)); } var result = await _LockManager.TryAcquireLock(name); if (result.Ok) { await using (result.Lock) { await Task.Delay(TimeSpan.FromSeconds(0.5)); } } }