Beispiel #1
0
        /// <summary>
        /// Attempts to acquire a lease based on the provided lease policy.
        /// </summary>
        /// <param name="leasePolicy">The configuration details for the lease.</param>
        /// <param name="leaseId">The id of the currently acquired lease.</param>
        /// <returns>A task for the async operation.</returns>
        public async Task <string> AcquireAsync(ILeasePolicy leasePolicy, string leaseId)
        {
            this.LeasePolicy = leasePolicy;

            await this.InitialiseAsync();

            if (!this.blob.Exists())
            {
                await Retriable.RetryAsync(async() =>
                {
                    using (var ms = new MemoryStream())
                    {
                        await this.blob.UploadFromStreamAsync(ms);
                    }
                });
            }

            try
            {
                return(await Retriable.RetryAsync(
                           () => this.blob.AcquireLeaseAsync(leasePolicy.Duration, leaseId),
                           new CancellationToken(),
                           new Count(10),
                           new DoNotRetryOnConflictPolicy()));
            }
            catch (StorageException exception)
            {
                if (exception.RequestInformation.HttpStatusCode == 409)
                {
                    throw new LeaseAcquisitionUnsuccessfulException(this.LeasePolicy, exception);
                }

                throw;
            }
        }
Beispiel #2
0
        private async Task <DataflowContext> DownloadFeedAsync(DataflowContext context)
        {
            try
            {
                await Retriable.RetryAsync(
                    async() =>
                {
                    if (File.Exists(context.Destination))
                    {
                        context.AlreadyDownloaded = true;

                        Console.WriteLine("Already Downloaded: " + context.Destination);

                        return;
                    }
                    else
                    {
                        var fileInfo = new FileInfo(context.Destination);

                        if (!fileInfo.Directory.Exists)
                        {
                            fileInfo.Directory.Create();
                        }
                    }

                    using (HttpClient client = this.httpClientFactory.CreateClient())
                    {
                        var request = new HttpRequestMessage(HttpMethod.Get, context.Source);

                        HttpResponseMessage response = await client.SendAsync(request).ConfigureAwait(false);

                        using (Stream streamToReadFrom = await response.Content.ReadAsStreamAsync().ConfigureAwait(false))
                        {
                            using (Stream streamToWriteTo = File.Open(context.Destination, FileMode.Create))
                            {
                                await streamToReadFrom.CopyToAsync(streamToWriteTo).ConfigureAwait(false);
                            }
                        }

                        Console.WriteLine("Downloaded: " + context.Destination);

                        response.EnsureSuccessStatusCode();
                    }
                },
                    CancellationToken.None,
                    new Backoff(5, TimeSpan.FromSeconds(1)),
                    new AnyException()).ConfigureAwait(false);

                context.IsFaulted = false;
            }
            catch (Exception ex)
            {
                context.IsFaulted  = true;
                context.FaultError = ex.Message;

                Console.WriteLine("Error Downloading: " + context.Destination);
            }

            return(context);
        }
        public Task ThenWithinSecondsTheFirstNotificationsStoredInTheTransientTenantForTheUserWithIdHaveTheDeliveryStatusForTheDeliveryChannelWithId(
            int timeoutSeconds,
            int count,
            string userId,
            UserNotificationDeliveryStatus expectedDeliveryStatus,
            string deliveryChannelId)
        {
            var tokenSource = new CancellationTokenSource();

            tokenSource.CancelAfter(TimeSpan.FromSeconds(timeoutSeconds));

            return(Retriable.RetryAsync(
                       async() =>
            {
                GetNotificationsResult userNotifications = await this.GetNotificationsForUserAsync(userId, count).ConfigureAwait(false);

                foreach (UserNotification current in userNotifications.Results)
                {
                    if (current.GetDeliveryStatusForChannel(deliveryChannelId) != expectedDeliveryStatus)
                    {
                        throw new Exception($"Notification with Id '{current.Id}' has delivery status '{current.GetDeliveryStatusForChannel(deliveryChannelId)}' instead of expected value '{expectedDeliveryStatus}'");
                    }
                }
            },
                       tokenSource.Token,
                       new Linear(TimeSpan.FromSeconds(5), int.MaxValue),
                       new AnyExceptionPolicy(),
                       false));
        }
Beispiel #4
0
        private async Task InitialiseAsync()
        {
            if (!this.initialised)
            {
                try
                {
                    this.storageAccount = this.GetStorageAccount();

                    this.client = Retriable.Retry(() => this.storageAccount.CreateCloudBlobClient());

                    string containerName = this.GetContainerName();
                    this.container = Retriable.Retry(() => this.client.GetContainerReference(containerName));

                    if (await this.container.CreateIfNotExistsAsync().ConfigureAwait(false))
                    {
                        var containerPermissions = new BlobContainerPermissions {
                            PublicAccess = BlobContainerPublicAccessType.Off
                        };

                        await Retriable.RetryAsync(() => this.container.SetPermissionsAsync(containerPermissions)).ConfigureAwait(false);
                    }

                    this.initialised = true;
                }
                catch (Exception ex)
                {
                    throw new InitializationFailureException("Initialization failed.", ex);
                }
            }
        }
Beispiel #5
0
        private static async Task RunAsync()
        {
            ISomeService someTasks = new MyService();

            var result = await Retriable.RetryAsync(() => SomeFuncAsync(someTasks));

            Console.WriteLine(result);
        }
Beispiel #6
0
        /// <summary>
        /// Attempts to extend the lease based on the lease policy provided to initially acquire it.
        /// </summary>
        /// <param name="leaseId">The id of the lease to attempt to extend.</param>
        /// <remarks>A valid lease and lease policy must exist for this operation to execute. An InvalidOperationException will be thrown otherwise.</remarks>
        /// <returns>A task for the async operation.</returns>
        public async Task ExtendAsync(string leaseId)
        {
            await this.InitialiseAsync();

            await Retriable.RetryAsync(() => this.blob.RenewLeaseAsync(new AccessCondition {
                LeaseId = leaseId
            }));
        }
 /// <summary>
 /// Executes the supplied method with standard retry strategy and policy.
 /// </summary>
 /// <typeparam name="T">The type of the return value.</typeparam>
 /// <param name="asyncMethod">The method to execute.</param>
 /// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
 public static Task <T> ExecuteWithStandardTestRetryRulesAsync <T>(Func <Task <T> > asyncMethod)
 {
     return(Retriable.RetryAsync(
                asyncMethod,
                CancellationToken.None,
                new UseCosmosRetryAfterHeaderStrategy(TimeSpan.FromSeconds(2), 5),
                RetryOnCosmosRequestRateExceededPolicy.Instance,
                false));
 }
 /// <summary>
 /// Executes the supplied method with standard retry strategy and policy.
 /// </summary>
 /// <param name="asyncMethod">The method to execute.</param>
 /// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
 public static Task ExecuteWithStandardTestRetryRulesAsync(Func <Task> asyncMethod)
 {
     return(Retriable.RetryAsync(
                asyncMethod,
                CancellationToken.None,
                new Linear(TimeSpan.FromSeconds(5), 5),
                RetryOnCosmosRequestRateExceededPolicy.Instance,
                false));
 }
Beispiel #9
0
 /// <inheritdoc/>
 public async Task UpsertWorkflowAsync(Workflow workflow, string partitionKey = null)
 {
     await Retriable.RetryAsync(
         () => this.UpsertWorkflowCoreAsync(workflow),
         CancellationToken.None,
         new Backoff(5, TimeSpan.FromSeconds(1)),
         DoNotRetryWhenFutile.Instance)
     .ConfigureAwait(false);
 }
Beispiel #10
0
 /// <inheritdoc/>
 public async Task <Workflow> GetWorkflowAsync(string workflowId, string partitionKey = null)
 {
     return(await Retriable.RetryAsync(
                () => this.GetWorkflowCoreAsync(workflowId),
                CancellationToken.None,
                new Backoff(5, TimeSpan.FromSeconds(1)),
                DoNotRetryWhenFutile.Instance)
            .ConfigureAwait(false));
 }
        /// <summary>
        /// Enumerate the entities matching a particular query.
        /// </summary>
        /// <typeparam name="T">The type of entity to enumerate.</typeparam>
        /// <param name="container">The Cosmos container against which to execute the query.</param>
        /// <param name="queryDefinition">The query definition.</param>
        /// <param name="actionAsync">The action to execute.</param>
        /// <param name="requestOptions">Request options for the query.</param>
        /// <param name="maxBatchCount">The maximum number of batches to process.</param>
        /// <param name="continuationToken">The continuation token from which to resume processing.</param>
        /// <param name="cancellationToken">A cancellation token to terminate the option early.</param>
        /// <returns>A <see cref="Task"/> which provides a continuation token if it terminates before .</returns>
        public static async Task <string?> ForEachAsync <T>(this Container container, QueryDefinition queryDefinition, Func <T, Task> actionAsync, QueryRequestOptions?requestOptions = null, int?maxBatchCount = null, string?continuationToken = null, CancellationToken cancellationToken = default)
        {
            if (container is null)
            {
                throw new ArgumentNullException(nameof(container));
            }

            if (queryDefinition == null)
            {
                throw new ArgumentNullException(nameof(queryDefinition));
            }

            if (actionAsync == null)
            {
                throw new ArgumentNullException(nameof(actionAsync));
            }

            FeedIterator <T> iterator = container.GetItemQueryIterator <T>(queryDefinition, continuationToken, requestOptions);

            int    batchCount = 0;
            string?previousContinuationToken = null;
            string?responseContinuationToken = null;

            try
            {
                while (iterator.HasMoreResults && (!maxBatchCount.HasValue || batchCount < maxBatchCount.Value))
                {
                    batchCount++;

                    FeedResponse <T> response = await Retriable.RetryAsync(
                        () => iterator.ReadNextAsync(cancellationToken),
                        CancellationToken.None,
                        new Backoff(3, TimeSpan.FromSeconds(1)),
                        RetryOnBusyPolicy.Instance,
                        false)
                                                .ConfigureAwait(false);

                    previousContinuationToken = responseContinuationToken;
                    responseContinuationToken = response.ContinuationToken;
                    foreach (T item in response)
                    {
                        await actionAsync(item).ConfigureAwait(false);

                        cancellationToken.ThrowIfCancellationRequested();
                    }
                }
            }
            catch (OperationCanceledException)
            {
                // We cancelled the operation so we revert to the previous continuation token to allow reprocessing from the previous batch
                responseContinuationToken = previousContinuationToken;
            }

            return(responseContinuationToken);
        }
        /// <inheritdoc/>
        public Task UpsertWorkflowAsync(Workflow workflow, string partitionKey = null)
        {
            return(Retriable.RetryAsync(() =>
            {
                if (string.IsNullOrEmpty(workflow.ETag))
                {
                    return this.CreateWorkflowAsync(workflow, partitionKey);
                }

                return this.UpdateWorkflowAsync(workflow, partitionKey);
            }));
        }
Beispiel #13
0
        public async Task ThenAfterSecondsAtMostTheWorkflowInstanceWithIdShouldBeInTheStateWithName(string instanceId, string expectedStateName, int maxWaitTime)
        {
            var tokenSource = new CancellationTokenSource();

            tokenSource.CancelAfter(TimeSpan.FromSeconds(maxWaitTime));
            await Retriable.RetryAsync(
                () => this.VerifyWorkflowInstanceState(instanceId, expectedStateName, false),
                tokenSource.Token,
                new Linear(TimeSpan.FromSeconds(1), int.MaxValue),
                new AnyExceptionPolicy(),
                false).ConfigureAwait(false);
        }
Beispiel #14
0
        public async Task ThenAfterSecondsAtMostThereShouldBeAWorkflowInstanceWithTheIdInTheWorkflowInstanceStore(string instanceId, int maximumWaitTime)
        {
            var tokenSource = new CancellationTokenSource();

            tokenSource.CancelAfter(TimeSpan.FromSeconds(maximumWaitTime));
            await Retriable.RetryAsync(
                () => this.GetWorkflowInstance(instanceId, false),
                tokenSource.Token,
                new Linear(TimeSpan.FromSeconds(1), int.MaxValue),
                new AnyExceptionPolicy(),
                false).ConfigureAwait(false);
        }
Beispiel #15
0
        /// <inheritdoc/>
        public async Task <int> GetMatchingWorkflowInstanceCountForSubjectsAsync(IEnumerable <string> subjects)
        {
            QueryDefinition spec = BuildFindInstanceIdsSpec(subjects, 1, 0, true);

            FeedIterator <int> iterator = this.Container.GetItemQueryIterator <int>(spec, null, new QueryRequestOptions {
                MaxItemCount = 1
            });

            // There will always be a result so we don't need to check...
            FeedResponse <int> result = await Retriable.RetryAsync(() => iterator.ReadNextAsync()).ConfigureAwait(false);

            return(result.First());
        }
Beispiel #16
0
        private static async Task RunInlineAsync()
        {
            ISomeService someTasks = new MyService();

            var result = await Retriable.RetryAsync(async delegate
            {
                var response = await someTasks.FirstTaskAsync();

                return(await someTasks.SecondTaskAsync(response));
            });

            Console.WriteLine(result);
        }
Beispiel #17
0
 /// <inheritdoc/>
 public async Task DeleteWorkflowInstanceAsync(string workflowInstanceId, string partitionKey = null)
 {
     try
     {
         await Retriable.RetryAsync(() =>
                                    this.Container.DeleteItemAsync <WorkflowInstance>(
                                        workflowInstanceId,
                                        new PartitionKey(partitionKey ?? workflowInstanceId)))
         .ConfigureAwait(false);
     }
     catch (CosmosException ex) when(ex.StatusCode == HttpStatusCode.NotFound)
     {
         throw new WorkflowInstanceNotFoundException($"The workflow instance with id {workflowInstanceId} was not found", ex);
     }
 }
Beispiel #18
0
        private async Task InitialiseAsync()
        {
            if (!this.isInitialised)
            {
                var storageAccount = CloudStorageAccount.Parse(Configuration.GetSettingFor(this.connectionStringProvider.ConnectionStringKey));

                var client = storageAccount.CreateCloudBlobClient();

                var container = client.GetContainerReference("endjin-leasing-leases");

                await Retriable.RetryAsync(container.CreateIfNotExistsAsync);

                this.blob = container.GetBlockBlobReference(this.LeasePolicy.Name.ToLowerInvariant());

                this.isInitialised = true;
            }
        }
        /// <inheritdoc/>
        public async Task <Workflow> GetWorkflowAsync(string workflowId, string partitionKey = null)
        {
            try
            {
                ItemResponse <Workflow> itemResponse = await Retriable.RetryAsync(() =>
                                                                                  this.Container.ReadItemAsync <Workflow>(
                                                                                      workflowId,
                                                                                      new PartitionKey(partitionKey ?? workflowId)))
                                                       .ConfigureAwait(false);

                return(itemResponse.Resource);
            }
            catch (CosmosException ex) when(ex.StatusCode == HttpStatusCode.NotFound)
            {
                throw new WorkflowNotFoundException($"The workflow with id {workflowId} was not found", ex);
            }
        }
Beispiel #20
0
        public async Task <Lease> GetAutoRenewingLeaseWithOptionsAsync(CancellationToken cancellationToken, string leaseName, string actorName = "")
        {
            var leaseProvider = this.leaseProviderFactory.Create();

            this.CheckProperties();

            var lease = new Lease(leaseProvider, this.leasePolicyValidator);

            await Retriable.RetryAsync(async() =>
            {
                await this.AcquireLeaseAndStartRenewingAsync(cancellationToken, leaseProvider, lease);
            },
                                       cancellationToken,
                                       this.RetryStrategy,
                                       this.RetryPolicy);

            return(lease);
        }
Beispiel #21
0
 /// <inheritdoc/>
 public async Task UpsertWorkflowInstanceAsync(WorkflowInstance workflowInstance, string partitionKey = null)
 {
     try
     {
         await Retriable.RetryAsync(() =>
                                    this.Container.UpsertItemAsync(
                                        workflowInstance,
                                        new PartitionKey(partitionKey ?? workflowInstance.Id),
                                        new ItemRequestOptions {
             IfMatchEtag = workflowInstance.ETag
         }))
         .ConfigureAwait(false);
     }
     catch (CosmosException ex) when(ex.StatusCode == HttpStatusCode.NotFound)
     {
         throw new WorkflowInstanceConflictException($"The workflow instance with id {workflowInstance.Id} was already modified.", ex);
     }
 }
Beispiel #22
0
        /// <inheritdoc/>
        public async Task ExtendAsync(Lease lease)
        {
            if (lease is null)
            {
                throw new ArgumentNullException(nameof(lease));
            }

            this.logger.LogDebug($"Extending lease for '{lease.LeasePolicy.ActorName}' with name '{lease.LeasePolicy.Name}', duration '{lease.LeasePolicy.Duration}', and actual id '{lease.Id}'");
            await this.InitialiseAsync().ConfigureAwait(false);

            CloudBlockBlob blob = this.container !.GetBlockBlobReference(lease.LeasePolicy.Name.ToLowerInvariant());
            await Retriable.RetryAsync(() => blob.RenewLeaseAsync(new AccessCondition {
                LeaseId = lease.Id
            })).ConfigureAwait(false);

            (lease as AzureLease)?.SetLastAcquired(DateTimeOffset.Now);
            this.logger.LogDebug($"Extended lease for '{lease.LeasePolicy.ActorName}' with name '{lease.LeasePolicy.Name}', duration '{lease.LeasePolicy.Duration}', and actual id '{lease.Id}'");
        }
Beispiel #23
0
        private async Task <Tuple <bool, T> > TryAcquireLeaseAndExecuteAsync <T>(Func <CancellationToken, Task <T> > action, CancellationTokenSource cancellationTokenSource, ILeaseProvider leaseProvider)
        {
            try
            {
                this.CheckProperties();

                var result = await Retriable.RetryAsync(
                    () => this.AcquireLeaseAndExecuteInnerAsync(action, cancellationTokenSource, leaseProvider),
                    cancellationTokenSource.Token,
                    this.RetryStrategy,
                    this.RetryPolicy);

                return(new Tuple <bool, T>(true, result));
            }
            catch (LeaseAcquisitionUnsuccessfulException)
            {
                return(new Tuple <bool, T>(false, default(T)));
            }
        }
 private async Task PublishWithRetryAsync <T>(string source, string subject, T cloudEvent, WorkflowEventSubscription destination)
 {
     try
     {
         await Retriable.RetryAsync(() => this.PublishAsync(source, subject, cloudEvent, destination)).ConfigureAwait(false);
     }
     catch (Exception ex)
     {
         // In this "v1" solution to event publishing, we don't want exceptions to propagate outside the
         // publisher because we don't want failure in event publishing to break anything.
         this.logger.LogError(
             ex,
             "Unexpected exception when trying to a CloudEvent with source '{source}', '{subject}' and destinationUrl '{destinationUrl}' with authentication resource '{msiAuthenticationResource}'.",
             source,
             subject,
             destination?.ExternalUrl,
             destination?.MsiAuthenticationResource);
     }
 }
Beispiel #25
0
        /// <inheritdoc/>
        public async Task <IEnumerable <string> > GetMatchingWorkflowInstanceIdsForSubjectsAsync(
            IEnumerable <string> subjects,
            int pageSize,
            int pageNumber)
        {
            QueryDefinition spec = BuildFindInstanceIdsSpec(subjects, pageSize, pageNumber);

            FeedIterator <dynamic> iterator = this.Container.GetItemQueryIterator <dynamic>(spec);

            var matchingIds = new List <string>();

            while (iterator.HasMoreResults)
            {
                FeedResponse <dynamic> results = await Retriable.RetryAsync(() => iterator.ReadNextAsync()).ConfigureAwait(false);

                matchingIds.AddRange(results.Select(x => (string)x.id));
            }

            return(matchingIds);
        }
        private Task LongRunningOperationPropertyCheck(Uri location, string operationPropertyPath, int timeoutSeconds, Action <string> testValue)
        {
            var tokenSource = new CancellationTokenSource();

            tokenSource.CancelAfter(TimeSpan.FromSeconds(timeoutSeconds));

            return(Retriable.RetryAsync(
                       async() =>
            {
                HttpResponseMessage response = await HttpClient.GetAsync(location).ConfigureAwait(false);
                string responseBody = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
                var operation = JObject.Parse(responseBody);
                JToken targetToken = operation.SelectToken(operationPropertyPath);
                string currentValue = targetToken.Value <string>();
                testValue(currentValue);
            },
                       tokenSource.Token,
                       new Linear(TimeSpan.FromSeconds(5), int.MaxValue),
                       new AnyExceptionPolicy(),
                       false));
        }
Beispiel #27
0
        private async Task <bool> TryAcquireLeaseAndExecuteAsync <T>(Func <CancellationToken, T, Task> action, CancellationTokenSource cancellationTokenSource, ILeaseProvider leaseProvider, T arg)
        {
            try
            {
                this.CheckProperties();

                await Retriable.RetryAsync(async() =>
                {
                    await this.AcquireLeaseAndExecuteInnerAsync(action, cancellationTokenSource, leaseProvider, arg);
                },
                                           cancellationTokenSource.Token,
                                           this.RetryStrategy,
                                           this.RetryPolicy);

                return(true);
            }
            catch (LeaseAcquisitionUnsuccessfulException)
            {
                return(false);
            }
        }
Beispiel #28
0
        /// <inheritdoc/>
        public async Task <WorkflowInstance> StartWorkflowInstanceAsync(
            string workflowId,
            string workflowPartitionKey          = null,
            string instanceId                    = null,
            string instancePartitionKey          = null,
            IDictionary <string, string> context = null)
        {
            // We need to clone the original context here in case it's modified by any of the code which creates the
            // workflow instance.
            var originalSuppliedContext = context?.ToDictionary(x => x.Key, x => x.Value);

            WorkflowInstance newInstance = await Retriable.RetryAsync(() =>
                                                                      this.CreateWorkflowInstanceAsync(workflowId, workflowPartitionKey, instanceId, instancePartitionKey, context)).ConfigureAwait(false);

            // We publish the CloudEvent outside the Retry block because:
            // a) we only want to publish the event once the instance is created, and
            // b) we don't want a failure in CloudEvent publishing to cause a retry
            //    of the whole process. The ICloudEventPublisher implementation is expected
            //    to provide its own retry mechanism.
            Workflow workflow = await this.workflowStore.GetWorkflowAsync(newInstance.WorkflowId).ConfigureAwait(false);

            var workflowEventData = new WorkflowInstanceCreationCloudEventData(newInstance.Id)
            {
                NewContext      = newInstance.Context,
                NewState        = newInstance.StateId,
                NewStatus       = newInstance.Status,
                SuppliedContext = originalSuppliedContext,
                WorkflowId      = newInstance.WorkflowId,
            };

            await this.cloudEventPublisher.PublishWorkflowEventDataAsync(
                this.cloudEventSource,
                WorkflowEventTypes.InstanceCreated,
                newInstance.Id,
                workflowEventData.ContentType,
                workflowEventData,
                workflow.WorkflowEventSubscriptions).ConfigureAwait(false);

            return(newInstance);
        }
Beispiel #29
0
        /// <inheritdoc/>
        public async Task ReleaseAsync(Lease lease)
        {
            if (lease is null)
            {
                throw new ArgumentNullException(nameof(lease));
            }

            if (lease is not AzureLease al)
            {
                throw new ArgumentException("Only Leases of type 'AzureLease' can be released by the AzureLeaseProvider.");
            }

            this.logger.LogDebug($"Releasing lease for '{lease.LeasePolicy.ActorName}' with name '{lease.LeasePolicy.Name}', duration '{lease.LeasePolicy.Duration}', and actual id '{lease.Id}'");
            await this.InitialiseAsync().ConfigureAwait(false);

            CloudBlockBlob blob = this.container !.GetBlockBlobReference(lease.LeasePolicy.Name.ToLowerInvariant());

            await Retriable.RetryAsync(() => blob.ReleaseLeaseAsync(new AccessCondition {
                LeaseId = lease.Id
            })).ConfigureAwait(false);

            al.SetLastAcquired(null);
            this.logger.LogDebug($"Released lease for '{lease.LeasePolicy.ActorName}' with name '{lease.LeasePolicy.Name}', duration '{lease.LeasePolicy.Duration}', and actual id '{lease.Id}'");
        }
Beispiel #30
0
        public async Task <Lease> GetAutoRenewingLeaseAsync(CancellationToken cancellationToken, string leaseName, string actorName = "")
        {
            var leaseProvider = this.leaseProviderFactory.Create();

            this.LeasePolicy = new LeasePolicy {
                Duration = leaseProvider.DefaultLeaseDuration, Name = leaseName, ActorName = actorName
            };
            this.RetryStrategy = new Linear(TimeSpan.FromSeconds(Math.Round(leaseProvider.DefaultLeaseDuration.TotalSeconds / 3)), int.MaxValue);
            this.RetryPolicy   = new RetryUntilLeaseAcquiredPolicy();

            this.CheckProperties();

            var lease = new Lease(leaseProvider, this.leasePolicyValidator);

            await Retriable.RetryAsync(async() =>
            {
                await this.AcquireLeaseAndStartRenewingAsync(cancellationToken, leaseProvider, lease);
            },
                                       cancellationToken,
                                       this.RetryStrategy,
                                       this.RetryPolicy);

            return(lease);
        }