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);
        }
        private static IServiceProvider InitializeAndGetServiceProvider()
        {
            ServicePointManager.DefaultConnectionLimit = 64;
            ServicePointManager.SecurityProtocol      &= ~SecurityProtocolType.Ssl3;
            ServicePointManager.SecurityProtocol      |= SecurityProtocolType.Tls12;

            var configurationRoot  = GetConfigurationRoot();
            var instrumentationKey = configurationRoot
                                     .GetSection(ConfigurationSectionName)
                                     .GetValue <string>(nameof(V3PerPackageConfiguration.InstrumentationKey));

            ApplicationInsights.Initialize(instrumentationKey);

            var loggerConfiguration = LoggingSetup.CreateDefaultLoggerConfiguration(withConsoleLogger: true);
            var loggerFactory       = LoggingSetup.CreateLoggerFactory(loggerConfiguration, LogEventLevel.Information);

            var serviceCollection = new ServiceCollection();

            serviceCollection.Configure <V3PerPackageConfiguration>(configurationRoot.GetSection(ConfigurationSectionName));
            serviceCollection.AddOptions();
            serviceCollection.Add(ServiceDescriptor.Scoped(typeof(IOptionsSnapshot <>), typeof(NonCachingOptionsSnapshot <>)));

            serviceCollection.AddLogging();
            serviceCollection.AddSingleton(loggerFactory);

            serviceCollection.AddSingleton(new ControlledDisposeHttpClientHandler());
            serviceCollection.AddTransient <HttpMessageHandler>(x => x.GetRequiredService <ControlledDisposeHttpClientHandler>());
            serviceCollection.AddSingleton(x => new HttpClient(x.GetRequiredService <HttpMessageHandler>()));
            serviceCollection.AddTransient <Func <HttpMessageHandler> >(x => () => x.GetRequiredService <HttpMessageHandler>());
            serviceCollection.AddTransient <PerBatchProcessor>();
            serviceCollection.AddTransient <ITelemetryService, TelemetryService>();
            serviceCollection.AddTransient <PerWorkerProcessor>();
            serviceCollection.AddTransient <PerProcessProcessor>();
            serviceCollection.AddSingleton <StringLocker>();
            serviceCollection.AddTransient <EnqueueCollector>();
            serviceCollection.AddTransient <EnqueueCommand>();
            serviceCollection.AddTransient <CleanUpCommand>();

            serviceCollection.AddSingleton(x =>
            {
                var globalContext = x.GetRequiredService <GlobalContext>();

                var perProcessContext = new PerProcessContext(
                    globalContext,
                    UniqueName.New("process"),
                    WorkerCount,
                    MessageCount,
                    BatchSize);

                var blobClient       = BlobStorageUtilities.GetBlobClient(globalContext);
                var flatContainerUrl = $"{blobClient.BaseUri.AbsoluteUri}/{globalContext.FlatContainerContainerName}/{perProcessContext.Name}";
                RegistrationMakerCatalogItem.PackagePathProvider = new FlatContainerPackagePathProvider(flatContainerUrl);

                return(perProcessContext);
            });

            serviceCollection.AddTransient(x =>
            {
                var settings = x.GetRequiredService <IOptionsSnapshot <V3PerPackageConfiguration> >();
                return(new GlobalContext(
                           settings.Value.StorageBaseAddress,
                           settings.Value.StorageAccountName,
                           settings.Value.StorageKeyValue,
                           settings.Value.ContentBaseAddress,
                           settings.Value.GalleryBaseAddress));
            });

            serviceCollection.AddSingleton(new TelemetryClient());

            serviceCollection.AddTransient <IStorageQueue <PackageMessage> >(x =>
            {
                var globalContext      = x.GetRequiredService <GlobalContext>();
                var storageCredentials = new StorageCredentials(globalContext.StorageAccountName, globalContext.StorageKeyValue);
                var storageAccount     = new CloudStorageAccount(storageCredentials, useHttps: true);

                return(new StorageQueue <PackageMessage>(
                           new AzureStorageQueue(storageAccount, "v3perpackage"),
                           new JsonMessageSerializer <PackageMessage>(JsonSerializerUtility.SerializerSettings),
                           PackageMessage.Version));
            });

            return(serviceCollection.BuildServiceProvider());
        }
        private async Task ProcessPerWorkerAsync(PerProcessContext processContext, int messageCount)
        {
            var context = new PerWorkerContext(processContext, UniqueName.New("worker"));

            await _perWorkerProcessor.ProcessAsync(context, messageCount);
        }