コード例 #1
0
        async Task RunLoopAsync(CancellationToken cancellationToken) // throws Exception, ExceptionWithAction
        {
            var loopStopwatch = new Stopwatch();

            while (!cancellationToken.IsCancellationRequested)
            {
                // Mark start time so we can use the duration taken to calculate renew interval.
                loopStopwatch.Restart();

                ILeaseManager leaseManager           = this.host.LeaseManager;
                Dictionary <string, Lease> allLeases = new Dictionary <string, Lease>();

                // Inspect all leases.
                // Acquire any expired leases.
                // Renew any leases that currently belong to us.
                IEnumerable <Task <Lease> > gettingAllLeases = leaseManager.GetAllLeases();
                List <Lease> leasesOwnedByOthers             = new List <Lease>();
                var          renewLeaseTasks = new List <Task>();
                int          ourLeaseCount   = 0;

                // First thing is first, renew owned leases.
                foreach (Task <Lease> getLeaseTask in gettingAllLeases)
                {
                    try
                    {
                        var lease = await getLeaseTask.ConfigureAwait(false);

                        allLeases[lease.PartitionId] = lease;
                        if (lease.Owner == this.host.HostName)
                        {
                            ourLeaseCount++;
                            ProcessorEventSource.Log.PartitionPumpInfo(this.host.HostName, lease.PartitionId, "Trying to renew lease.");
                            renewLeaseTasks.Add(leaseManager.RenewLeaseAsync(lease).ContinueWith(renewResult =>
                            {
                                if (renewResult.IsFaulted || !renewResult.Result)
                                {
                                    // Might have failed due to intermittent error or lease-lost.
                                    // Just log here, expired leases will be picked by same or another host anyway.
                                    ProcessorEventSource.Log.PartitionPumpError(this.host.HostName, lease.PartitionId, "Failed to renew lease.", renewResult.Exception?.Message);
                                    this.host.EventProcessorOptions.NotifyOfException(this.host.HostName, lease.PartitionId, renewResult.Exception, EventProcessorHostActionStrings.RenewingLease);
                                }
                            }));
                        }
                        else
                        {
                            leasesOwnedByOthers.Add(lease);
                        }
                    }
                    catch (Exception e)
                    {
                        ProcessorEventSource.Log.EventProcessorHostError(this.host.HostName, "Failure during checking lease.", e.ToString());
                        this.host.EventProcessorOptions.NotifyOfException(this.host.HostName, "N/A", e, EventProcessorHostActionStrings.CheckingLeases);
                    }
                }

                // Wait until we are done with renewing our own leases here.
                // In theory, this should never throw, error are logged and notified in the renew tasks.
                await Task.WhenAll(renewLeaseTasks.ToArray()).ConfigureAwait(false);

                ProcessorEventSource.Log.EventProcessorHostInfo(this.host.HostName, "Lease renewal is finished.");

                // Check any expired leases that we can grab here.
                foreach (var possibleLease in allLeases.Values)
                {
                    try
                    {
                        if (await possibleLease.IsExpired().ConfigureAwait(false))
                        {
                            bool isExpiredLeaseOwned = possibleLease.Owner == this.host.HostName;
                            ProcessorEventSource.Log.PartitionPumpInfo(this.host.HostName, possibleLease.PartitionId, "Trying to acquire lease.");
                            if (await leaseManager.AcquireLeaseAsync(possibleLease).ConfigureAwait(false))
                            {
                                ProcessorEventSource.Log.PartitionPumpInfo(this.host.HostName, possibleLease.PartitionId, "Acquired lease.");

                                // Don't double count if we have already counted this lease at the beginning of the loop.
                                if (!isExpiredLeaseOwned)
                                {
                                    ourLeaseCount++;
                                }
                            }
                        }
                    }
                    catch (Exception e)
                    {
                        ProcessorEventSource.Log.PartitionPumpError(this.host.HostName, possibleLease.PartitionId, "Failure during acquiring lease", e.ToString());
                        this.host.EventProcessorOptions.NotifyOfException(this.host.HostName, possibleLease.PartitionId, e, EventProcessorHostActionStrings.CheckingLeases);
                    }
                }

                // Grab more leases if available and needed for load balancing
                if (leasesOwnedByOthers.Count > 0)
                {
                    Lease stealThisLease = WhichLeaseToSteal(leasesOwnedByOthers, ourLeaseCount);
                    if (stealThisLease != null)
                    {
                        try
                        {
                            ProcessorEventSource.Log.PartitionPumpStealLeaseStart(this.host.HostName, stealThisLease.PartitionId);
                            if (await leaseManager.AcquireLeaseAsync(stealThisLease).ConfigureAwait(false))
                            {
                                // Succeeded in stealing lease
                                ProcessorEventSource.Log.PartitionPumpStealLeaseStop(this.host.HostName, stealThisLease.PartitionId);
                            }
                            else
                            {
                                ProcessorEventSource.Log.EventProcessorHostWarning(this.host.HostName,
                                                                                   "Failed to steal lease for partition " + stealThisLease.PartitionId, null);
                            }
                        }
                        catch (Exception e)
                        {
                            ProcessorEventSource.Log.EventProcessorHostError(this.host.HostName,
                                                                             "Exception during stealing lease for partition " + stealThisLease.PartitionId, e.ToString());
                            this.host.EventProcessorOptions.NotifyOfException(this.host.HostName,
                                                                              stealThisLease.PartitionId, e, EventProcessorHostActionStrings.StealingLease);
                        }
                    }
                }

                // Update pump with new state of leases.
                foreach (string partitionId in allLeases.Keys)
                {
                    try
                    {
                        Lease updatedLease = allLeases[partitionId];
                        ProcessorEventSource.Log.EventProcessorHostInfo(this.host.HostName, $"Lease on partition {updatedLease.PartitionId} owned by {updatedLease.Owner}");
                        if (updatedLease.Owner == this.host.HostName)
                        {
                            await this.CheckAndAddPumpAsync(partitionId, updatedLease).ConfigureAwait(false);
                        }
                        else
                        {
                            await this.RemovePumpAsync(partitionId, CloseReason.LeaseLost).ConfigureAwait(false);
                        }
                    }
                    catch (Exception e)
                    {
                        ProcessorEventSource.Log.EventProcessorHostError(this.host.HostName, $"Exception during add/remove pump on partition {partitionId}", e.Message);
                        this.host.EventProcessorOptions.NotifyOfException(this.host.HostName, partitionId, e, EventProcessorHostActionStrings.PartitionPumpManagement);
                    }
                }

                try
                {
                    // Consider reducing the wait time with last lease-walkthrough's time taken.
                    var elapsedTime = loopStopwatch.Elapsed;
                    if (leaseManager.LeaseRenewInterval > elapsedTime)
                    {
                        await Task.Delay(leaseManager.LeaseRenewInterval.Subtract(elapsedTime), cancellationToken).ConfigureAwait(false);
                    }
                }
                catch (TaskCanceledException)
                {
                    // Bail on the async work if we are canceled.
                }
            }
        }
コード例 #2
0
        async Task RunLoopAsync(CancellationToken cancellationToken) // throws Exception, ExceptionWithAction
        {
            while (!cancellationToken.IsCancellationRequested)
            {
                ILeaseManager leaseManager           = this.host.LeaseManager;
                Dictionary <string, Lease> allLeases = new Dictionary <string, Lease>();

                // Inspect all leases.
                // Acquire any expired leases.
                // Renew any leases that currently belong to us.
                IEnumerable <Task <Lease> > gettingAllLeases = leaseManager.GetAllLeases();
                List <Lease> leasesOwnedByOthers             = new List <Lease>();
                int          ourLeaseCount = 0;
                foreach (Task <Lease> getLeaseTask in gettingAllLeases)
                {
                    try
                    {
                        Lease possibleLease = await getLeaseTask.ConfigureAwait(false);

                        allLeases[possibleLease.PartitionId] = possibleLease;
                        if (await possibleLease.IsExpired().ConfigureAwait(false))
                        {
                            ProcessorEventSource.Log.PartitionPumpInfo(this.host.Id, possibleLease.PartitionId, "Trying to acquire lease.");
                            if (await leaseManager.AcquireLeaseAsync(possibleLease).ConfigureAwait(false))
                            {
                                ourLeaseCount++;
                            }
                            else
                            {
                                // Probably failed because another host stole it between get and acquire
                                leasesOwnedByOthers.Add(possibleLease);
                            }
                        }
                        else if (possibleLease.Owner == this.host.HostName)
                        {
                            ProcessorEventSource.Log.PartitionPumpInfo(this.host.Id, possibleLease.PartitionId, "Trying to renew lease.");

                            // Try to renew the lease. If successful then this lease belongs to us,
                            // if throws LeaseLostException then we don't own it anymore.
                            try
                            {
                                await leaseManager.RenewLeaseAsync(possibleLease).ConfigureAwait(false);

                                ourLeaseCount++;
                            }
                            catch (LeaseLostException)
                            {
                                // Probably failed because another host stole it between get and renew
                                leasesOwnedByOthers.Add(possibleLease);
                            }
                        }
                        else
                        {
                            leasesOwnedByOthers.Add(possibleLease);
                        }
                    }
                    catch (Exception e)
                    {
                        ProcessorEventSource.Log.EventProcessorHostWarning(this.host.Id, "Failure during getting/acquiring/renewing lease, skipping", e.ToString());
                        this.host.EventProcessorOptions.NotifyOfException(this.host.HostName, "N/A", e, EventProcessorHostActionStrings.CheckingLeases);
                    }
                }

                // Grab more leases if available and needed for load balancing
                if (leasesOwnedByOthers.Count > 0)
                {
                    Lease stealThisLease = WhichLeaseToSteal(leasesOwnedByOthers, ourLeaseCount);
                    if (stealThisLease != null)
                    {
                        try
                        {
                            ProcessorEventSource.Log.PartitionPumpStealLeaseStart(this.host.Id, stealThisLease.PartitionId);
                            if (await leaseManager.AcquireLeaseAsync(stealThisLease).ConfigureAwait(false))
                            {
                                // Succeeded in stealing lease
                                ProcessorEventSource.Log.PartitionPumpStealLeaseStop(this.host.Id, stealThisLease.PartitionId);
                            }
                            else
                            {
                                ProcessorEventSource.Log.EventProcessorHostWarning(this.host.Id,
                                                                                   "Failed to steal lease for partition " + stealThisLease.PartitionId, null);
                            }
                        }
                        catch (Exception e)
                        {
                            ProcessorEventSource.Log.EventProcessorHostError(this.host.Id,
                                                                             "Exception during stealing lease for partition " + stealThisLease.PartitionId, e.ToString());
                            this.host.EventProcessorOptions.NotifyOfException(this.host.HostName,
                                                                              stealThisLease.PartitionId, e, EventProcessorHostActionStrings.StealingLease);
                        }
                    }
                }

                // Update pump with new state of leases.
                foreach (string partitionId in allLeases.Keys)
                {
                    try
                    {
                        Lease updatedLease = allLeases[partitionId];
                        ProcessorEventSource.Log.EventProcessorHostInfo(this.host.Id, $"Lease on partition {updatedLease.PartitionId} owned by {updatedLease.Owner}");
                        if (updatedLease.Owner == this.host.HostName)
                        {
                            await this.CheckAndAddPumpAsync(partitionId, updatedLease).ConfigureAwait(false);
                        }
                        else
                        {
                            await this.RemovePumpAsync(partitionId, CloseReason.LeaseLost).ConfigureAwait(false);
                        }
                    }
                    catch (Exception e)
                    {
                        ProcessorEventSource.Log.EventProcessorHostError(this.host.Id, $"Exception during add/remove pump on partition {partitionId}", e.Message);
                        this.host.EventProcessorOptions.NotifyOfException(this.host.HostName, partitionId, e, EventProcessorHostActionStrings.PartitionPumpManagement);
                    }
                }

                try
                {
                    await Task.Delay(leaseManager.LeaseRenewInterval, cancellationToken).ConfigureAwait(false);
                }
                catch (TaskCanceledException)
                {
                    // Bail on the async work if we are canceled.
                }
            }
        }
コード例 #3
0
        async Task RunLoopAsync(CancellationToken cancellationToken) // throws Exception, ExceptionWithAction
        {
            var loopStopwatch = new Stopwatch();

            while (!cancellationToken.IsCancellationRequested)
            {
                // Mark start time so we can use the duration taken to calculate renew interval.
                loopStopwatch.Restart();

                ILeaseManager leaseManager        = this.host.LeaseManager;
                var           allLeases           = new ConcurrentDictionary <string, Lease>();
                var           leasesOwnedByOthers = new ConcurrentDictionary <string, Lease>();

                // Inspect all leases.
                // Acquire any expired leases.
                // Renew any leases that currently belong to us.
                IEnumerable <Lease> downloadedLeases;
                var renewLeaseTasks = new List <Task>();
                int ourLeaseCount   = 0;

                try
                {
                    try
                    {
                        downloadedLeases = await leaseManager.GetAllLeasesAsync().ConfigureAwait(false);
                    }
                    catch (Exception e)
                    {
                        ProcessorEventSource.Log.EventProcessorHostError(this.host.HostName, "Exception during downloading leases", e.Message);
                        this.host.EventProcessorOptions.NotifyOfException(this.host.HostName, "N/A", e, EventProcessorHostActionStrings.DownloadingLeases);

                        // Avoid tight spin if getallleases call keeps failing.
                        await Task.Delay(1000).ConfigureAwait(false);

                        continue;
                    }

                    // First things first, renew owned leases.
                    foreach (var lease in downloadedLeases)
                    {
                        var subjectLease = lease;

                        try
                        {
                            allLeases[subjectLease.PartitionId] = subjectLease;
                            if (subjectLease.Owner == this.host.HostName)
                            {
                                ourLeaseCount++;

                                // Get lease from partition since we need the token at this point.
                                if (!this.partitionPumps.TryGetValue(subjectLease.PartitionId, out var capturedPump))
                                {
                                    continue;
                                }

                                var capturedLease = capturedPump.Lease;

                                ProcessorEventSource.Log.PartitionPumpInfo(this.host.HostName, capturedLease.PartitionId, "Trying to renew lease.");
                                renewLeaseTasks.Add(leaseManager.RenewLeaseAsync(capturedLease).ContinueWith(renewResult =>
                                {
                                    if (renewResult.IsFaulted)
                                    {
                                        // Might have failed due to intermittent error or lease-lost.
                                        // Just log here, expired leases will be picked by same or another host anyway.
                                        ProcessorEventSource.Log.PartitionPumpError(
                                            this.host.HostName,
                                            capturedLease.PartitionId,
                                            "Failed to renew lease.",
                                            renewResult.Exception?.Message);

                                        this.host.EventProcessorOptions.NotifyOfException(
                                            this.host.HostName,
                                            capturedLease.PartitionId,
                                            renewResult.Exception,
                                            EventProcessorHostActionStrings.RenewingLease);

                                        // Nullify the owner on the lease in case this host lost it.
                                        // This helps to remove pump earlier reducing duplicate receives.
                                        if (renewResult.Exception?.GetBaseException() is LeaseLostException)
                                        {
                                            allLeases[capturedLease.PartitionId].Owner = null;
                                        }
                                    }
                                }, cancellationToken));
                            }
                            else if (!await subjectLease.IsExpired().ConfigureAwait(false))
                            {
                                leasesOwnedByOthers[subjectLease.PartitionId] = subjectLease;
                            }
                        }
                        catch (Exception e)
                        {
                            ProcessorEventSource.Log.EventProcessorHostError(this.host.HostName, "Failure during checking lease.", e.ToString());
                            this.host.EventProcessorOptions.NotifyOfException(this.host.HostName, "N/A", e, EventProcessorHostActionStrings.CheckingLeases);
                        }
                    }

                    // Wait until we are done with renewing our own leases here.
                    // In theory, this should never throw, error are logged and notified in the renew tasks.
                    await Task.WhenAll(renewLeaseTasks).ConfigureAwait(false);

                    ProcessorEventSource.Log.EventProcessorHostInfo(this.host.HostName, "Lease renewal is finished.");

                    // Check any expired leases that we can grab here.
                    var expiredLeaseTasks = new List <Task>();
                    foreach (var possibleLease in allLeases.Values)
                    {
                        var subjectLease = possibleLease;

                        if (await subjectLease.IsExpired().ConfigureAwait(false))
                        {
                            expiredLeaseTasks.Add(Task.Run(async() =>
                            {
                                try
                                {
                                    // Get fresh content of lease subject to acquire.
                                    var downloadedLease = await leaseManager.GetLeaseAsync(subjectLease.PartitionId).ConfigureAwait(false);
                                    allLeases[subjectLease.PartitionId] = downloadedLease;

                                    // Check expired once more here incase another host have already leased this since we populated the list.
                                    if (await downloadedLease.IsExpired().ConfigureAwait(false))
                                    {
                                        ProcessorEventSource.Log.PartitionPumpInfo(this.host.HostName, downloadedLease.PartitionId, "Trying to acquire lease.");
                                        if (await leaseManager.AcquireLeaseAsync(downloadedLease).ConfigureAwait(false))
                                        {
                                            ProcessorEventSource.Log.PartitionPumpInfo(this.host.HostName, downloadedLease.PartitionId, "Acquired lease.");
                                            leasesOwnedByOthers.TryRemove(downloadedLease.PartitionId, out var removedLease);
                                            Interlocked.Increment(ref ourLeaseCount);
                                        }
                                        else
                                        {
                                            // Acquisition failed. Make sure we don't leave the lease as owned.
                                            allLeases[subjectLease.PartitionId].Owner = null;

                                            ProcessorEventSource.Log.EventProcessorHostWarning(this.host.HostName,
                                                                                               "Failed to acquire lease for partition " + downloadedLease.PartitionId, null);
                                        }
                                    }
                                }
                                catch (Exception e)
                                {
                                    ProcessorEventSource.Log.PartitionPumpError(this.host.HostName, subjectLease.PartitionId, "Failure during acquiring lease", e.ToString());
                                    this.host.EventProcessorOptions.NotifyOfException(this.host.HostName, subjectLease.PartitionId, e, EventProcessorHostActionStrings.CheckingLeases);

                                    // Acquisition failed. Make sure we don't leave the lease as owned.
                                    allLeases[subjectLease.PartitionId].Owner = null;
                                }
                            }, cancellationToken));
                        }
                    }

                    await Task.WhenAll(expiredLeaseTasks).ConfigureAwait(false);

                    ProcessorEventSource.Log.EventProcessorHostInfo(this.host.HostName, "Expired lease check is finished.");

                    // Grab more leases if available and needed for load balancing
                    if (leasesOwnedByOthers.Count > 0)
                    {
                        Lease stealThisLease = WhichLeaseToSteal(leasesOwnedByOthers.Values, ourLeaseCount);

                        // Don't attempt to steal the lease if current host has a pump for this partition id
                        // This is possible when current pump is in failed state due to lease moved to some other host.
                        if (stealThisLease != null && !this.partitionPumps.ContainsKey(stealThisLease.PartitionId))
                        {
                            try
                            {
                                // Get fresh content of lease subject to acquire.
                                var downloadedLease = await leaseManager.GetLeaseAsync(stealThisLease.PartitionId).ConfigureAwait(false);

                                allLeases[stealThisLease.PartitionId] = downloadedLease;

                                // Don't attempt to steal if lease is already expired.
                                // Expired leases are picked up by other hosts quickly.
                                // Don't attempt to steal if owner has changed from the calculation time to refresh time.
                                if (!await downloadedLease.IsExpired().ConfigureAwait(false) &&
                                    downloadedLease.Owner == stealThisLease.Owner)
                                {
                                    ProcessorEventSource.Log.PartitionPumpStealLeaseStart(this.host.HostName, downloadedLease.PartitionId);
                                    if (await leaseManager.AcquireLeaseAsync(downloadedLease).ConfigureAwait(false))
                                    {
                                        // Succeeded in stealing lease
                                        ProcessorEventSource.Log.PartitionPumpStealLeaseStop(this.host.HostName, downloadedLease.PartitionId);
                                        ourLeaseCount++;
                                    }
                                    else
                                    {
                                        // Acquisition failed. Make sure we don't leave the lease as owned.
                                        allLeases[stealThisLease.PartitionId].Owner = null;

                                        ProcessorEventSource.Log.EventProcessorHostWarning(this.host.HostName,
                                                                                           "Failed to steal lease for partition " + downloadedLease.PartitionId, null);
                                    }
                                }
                            }
                            catch (Exception e)
                            {
                                ProcessorEventSource.Log.EventProcessorHostError(this.host.HostName,
                                                                                 "Exception during stealing lease for partition " + stealThisLease.PartitionId, e.ToString());
                                this.host.EventProcessorOptions.NotifyOfException(this.host.HostName,
                                                                                  stealThisLease.PartitionId, e, EventProcessorHostActionStrings.StealingLease);

                                // Acquisition failed. Make sure we don't leave the lease as owned.
                                allLeases[stealThisLease.PartitionId].Owner = null;
                            }
                        }
                    }

                    // Update pump with new state of leases on owned partitions in parallel.
                    var createRemovePumpTasks = new List <Task>();
                    foreach (string partitionId in allLeases.Keys)
                    {
                        var subjectPartitionId = partitionId;

                        Lease updatedLease = allLeases[subjectPartitionId];
                        ProcessorEventSource.Log.EventProcessorHostInfo(this.host.HostName, $"Lease on partition {updatedLease.PartitionId} owned by {updatedLease.Owner}");

                        if (updatedLease.Owner == this.host.HostName)
                        {
                            createRemovePumpTasks.Add(Task.Run(async() =>
                            {
                                try
                                {
                                    await this.CheckAndAddPumpAsync(subjectPartitionId, updatedLease).ConfigureAwait(false);
                                }
                                catch (Exception e)
                                {
                                    ProcessorEventSource.Log.EventProcessorHostError(this.host.HostName, $"Exception during add pump on partition {subjectPartitionId}", e.Message);
                                    this.host.EventProcessorOptions.NotifyOfException(this.host.HostName, subjectPartitionId, e, EventProcessorHostActionStrings.PartitionPumpManagement);
                                }
                            }, cancellationToken));
                        }
                        else if (this.partitionPumps.ContainsKey(partitionId))
                        {
                            createRemovePumpTasks.Add(Task.Run(async() =>
                            {
                                try
                                {
                                    await this.TryRemovePumpAsync(subjectPartitionId, CloseReason.LeaseLost).ConfigureAwait(false);
                                }
                                catch (Exception e)
                                {
                                    ProcessorEventSource.Log.EventProcessorHostError(this.host.HostName, $"Exception during remove pump on partition {subjectPartitionId}", e.Message);
                                    this.host.EventProcessorOptions.NotifyOfException(this.host.HostName, subjectPartitionId, e, EventProcessorHostActionStrings.PartitionPumpManagement);
                                }
                            }, cancellationToken));
                        }
                    }

                    await Task.WhenAll(createRemovePumpTasks).ConfigureAwait(false);

                    ProcessorEventSource.Log.EventProcessorHostInfo(this.host.HostName, "Pump update is finished.");
                }
                catch (Exception e)
                {
                    // TaskCancelledException is expected furing host unregister.
                    if (e is TaskCanceledException)
                    {
                        continue;
                    }

                    // Loop should not exit unless signalled via cancellation token. Log any failures and continue.
                    ProcessorEventSource.Log.EventProcessorHostError(this.host.HostName, "Exception from partition manager main loop, continuing", e.Message);
                    this.host.EventProcessorOptions.NotifyOfException(this.host.HostName, "N/A", e, EventProcessorHostActionStrings.PartitionPumpManagement);
                }
                finally
                {
                    // Consider reducing the wait time with last lease-walkthrough's time taken.
                    var elapsedTime = loopStopwatch.Elapsed;
                    if (leaseManager.LeaseRenewInterval > elapsedTime)
                    {
                        await Task.Delay(leaseManager.LeaseRenewInterval.Subtract(elapsedTime), cancellationToken).ConfigureAwait(false);
                    }
                }
            }
        }