private async Task <bool> ProcessNextMessageAsync(PerWorkerContext workerContext, int batchSize)
        {
            var batchContext = new PerBatchContext(workerContext, UniqueName.New("batch"));

            var packageIdentities = new HashSet <string>(StringComparer.OrdinalIgnoreCase);
            var messages          = new List <StorageQueueMessage <PackageMessage> >();
            var packageContexts   = new List <PerPackageContext>();
            StorageQueueMessage <PackageMessage> lastMessage;

            do
            {
                lastMessage = await _queue.GetNextAsync(CancellationToken.None);

                if (lastMessage != null)
                {
                    var packageId       = lastMessage.Contents.PackageId.Trim();
                    var packageVersion  = NuGetVersion.Parse(lastMessage.Contents.PackageVersion.Trim()).ToNormalizedString();
                    var packageIdentity = $"{packageId}/{packageVersion}";

                    // If this is a duplicate package, complete it and skip it.
                    if (!packageIdentities.Add(packageIdentity))
                    {
                        await _queue.RemoveAsync(lastMessage, CancellationToken.None);

                        continue;
                    }

                    messages.Add(lastMessage);
                    packageContexts.Add(new PerPackageContext(batchContext, packageId, packageVersion));
                }
            }while (messages.Count < batchSize && lastMessage != null && lastMessage.DequeueCount < 10);

            if (packageContexts.Count == 0)
            {
                return(false);
            }

            var complete = await ProcessPackagesAsync(batchContext, packageContexts);

            if (complete)
            {
                foreach (var message in messages)
                {
                    try
                    {
                        await _queue.RemoveAsync(message, CancellationToken.None);
                    }
                    catch (StorageException ex) when(ex.RequestInformation?.HttpStatusCode == (int)HttpStatusCode.NotFound)
                    {
                        // Ignore this error. The message has already been removed.
                    }
                }
            }

            return(true);
        }
        public async Task ProcessAsync(PerWorkerContext workerContext, int messageCount)
        {
            bool hasMoreMessages;
            var  processed = 0;

            do
            {
                var currentBatchSize = Math.Min(workerContext.Process.BatchSize, messageCount - processed);
                hasMoreMessages = await ProcessNextMessageAsync(workerContext, currentBatchSize);

                processed += currentBatchSize;
            }while (hasMoreMessages && processed < messageCount);
        }
        private async Task ProcessPerWorkerAsync(PerProcessContext processContext, int messageCount)
        {
            var context = new PerWorkerContext(processContext, UniqueName.New("worker"));

            await _perWorkerProcessor.ProcessAsync(context, messageCount);
        }