async Task SendMessagesPump() { try { Events.StartSendMessagesPump(this); int batchSize = this.options.BatchSize * this.Endpoint.FanOutFactor; // Keep the stores and prefetchers for each priority loaded // for the duration of the pump var messageProviderPairs = new Dictionary <uint, (IMessageIterator, StoreMessagesProvider)>(); // Outer loop to maintain the message pump until the executor shuts down while (!this.cts.IsCancellationRequested) { try { await this.hasMessagesInQueue.WaitAsync(this.options.BatchTimeout); SortedDictionary <uint, EndpointExecutorFsm> snapshot = this.prioritiesToFsms; bool haveMessagesRemaining = false; // Iterate through all the message queues in priority order foreach (KeyValuePair <uint, EndpointExecutorFsm> entry in snapshot) { // Also check for cancellation in every inner loop, // since it could take time to send a batch of messages if (this.cts.IsCancellationRequested) { break; } uint priority = entry.Key; EndpointExecutorFsm fsm = entry.Value; // Update the lastUsedFsm to be the current FSM this.lastUsedFsm = fsm; (IMessageIterator, StoreMessagesProvider)pair; if (!messageProviderPairs.TryGetValue(priority, out pair)) { // Create and cache a new pair for the message provider // so we can reuse it every loop pair.Item1 = this.messageStore.GetMessageIterator(GetMessageQueueId(this.Endpoint.Id, priority), fsm.Checkpointer.Offset + 1); pair.Item2 = new StoreMessagesProvider(pair.Item1, batchSize); messageProviderPairs.Add(priority, pair); } StoreMessagesProvider storeMessagesProvider = pair.Item2; IMessage[] messages = await storeMessagesProvider.GetMessages(); if (messages.Length > 0) { // Tag the message with the priority that we're currently // processing, so it can be used by metrics later messages.Select(m => m.ProcessedPriority = priority); Events.ProcessingMessages(this, messages, priority); await this.ProcessMessages(messages, fsm); Events.SendMessagesSuccess(this, messages, priority); MetricsV0.DrainedCountIncrement(this.Endpoint.Id, messages.Length, priority); // Only move on to the next priority if the queue for the current // priority is empty. If we processed any messages, break out of // the inner loop to restart at the beginning of the priorities list // again. This is so we can catch and process any higher priority // messages that came in while we were sending the current batch haveMessagesRemaining = true; break; } } if (!haveMessagesRemaining) { // All the message queues have been drained, reset the hasMessagesInQueue flag. this.hasMessagesInQueue.Reset(); } } catch (Exception ex) { Events.SendMessagesError(this, ex); // Swallow exception and keep trying. } } } catch (Exception ex) { Events.SendMessagesPumpFailure(this, ex); } }
static async Task <int> MainAsync(IConfigurationRoot configuration) { string logLevel = configuration.GetValue($"{Logger.RuntimeLogLevelEnvKey}", "info"); Logger.SetLogLevel(logLevel); // Set the LoggerFactory used by the Routing code. if (configuration.GetValue("EnableRoutingLogging", false)) { Routing.LoggerFactory = Logger.Factory; } ILogger logger = Logger.Factory.CreateLogger("EdgeHub"); EdgeHubCertificates certificates = await EdgeHubCertificates.LoadAsync(configuration, logger); bool clientCertAuthEnabled = configuration.GetValue(Constants.ConfigKey.EdgeHubClientCertAuthEnabled, false); string sslProtocolsConfig = configuration.GetValue(Constants.ConfigKey.SslProtocols, string.Empty); SslProtocols sslProtocols = SslProtocolsHelper.Parse(sslProtocolsConfig, DefaultSslProtocols, logger); logger.LogInformation($"Enabling SSL protocols: {sslProtocols.Print()}"); IDependencyManager dependencyManager = new DependencyManager(configuration, certificates.ServerCertificate, certificates.TrustBundle, sslProtocols); Hosting hosting = Hosting.Initialize(configuration, certificates.ServerCertificate, dependencyManager, clientCertAuthEnabled, sslProtocols); IContainer container = hosting.Container; logger.LogInformation("Initializing Edge Hub"); LogLogo(logger); LogVersionInfo(logger); logger.LogInformation($"OptimizeForPerformance={configuration.GetValue("OptimizeForPerformance", true)}"); logger.LogInformation($"MessageAckTimeoutSecs={configuration.GetValue("MessageAckTimeoutSecs", 30)}"); logger.LogInformation("Loaded server certificate with expiration date of {0}", certificates.ServerCertificate.NotAfter.ToString("o")); var metricsProvider = container.Resolve <IMetricsProvider>(); Metrics.InitWithAspNet(metricsProvider, logger); // Note this requires App.UseMetricServer() to be called in Startup.cs // Init V0 Metrics MetricsV0.BuildMetricsCollector(configuration); // EdgeHub and CloudConnectionProvider have a circular dependency. So need to Bind the EdgeHub to the CloudConnectionProvider. IEdgeHub edgeHub = await container.Resolve <Task <IEdgeHub> >(); ICloudConnectionProvider cloudConnectionProvider = await container.Resolve <Task <ICloudConnectionProvider> >(); cloudConnectionProvider.BindEdgeHub(edgeHub); // EdgeHub cloud proxy and DeviceConnectivityManager have a circular dependency, // so the cloud proxy has to be set on the DeviceConnectivityManager after both have been initialized. var deviceConnectivityManager = container.Resolve <IDeviceConnectivityManager>(); IConnectionManager connectionManager = await container.Resolve <Task <IConnectionManager> >(); (deviceConnectivityManager as DeviceConnectivityManager)?.SetConnectionManager(connectionManager); // Register EdgeHub credentials var edgeHubCredentials = container.ResolveNamed <IClientCredentials>("EdgeHubCredentials"); ICredentialsCache credentialsCache = await container.Resolve <Task <ICredentialsCache> >(); await credentialsCache.Add(edgeHubCredentials); // Initializing configuration logger.LogInformation("Initializing configuration"); IConfigSource configSource = await container.Resolve <Task <IConfigSource> >(); ConfigUpdater configUpdater = await container.Resolve <Task <ConfigUpdater> >(); await configUpdater.Init(configSource); if (!Enum.TryParse(configuration.GetValue("AuthenticationMode", string.Empty), true, out AuthenticationMode authenticationMode) || authenticationMode != AuthenticationMode.Cloud) { ConnectionReauthenticator connectionReauthenticator = await container.Resolve <Task <ConnectionReauthenticator> >(); connectionReauthenticator.Init(); } TimeSpan shutdownWaitPeriod = TimeSpan.FromSeconds(configuration.GetValue("ShutdownWaitPeriod", DefaultShutdownWaitPeriod)); (CancellationTokenSource cts, ManualResetEventSlim completed, Option <object> handler) = ShutdownHandler.Init(shutdownWaitPeriod, logger); using (IProtocolHead protocolHead = await GetEdgeHubProtocolHeadAsync(logger, configuration, container, hosting)) using (var renewal = new CertificateRenewal(certificates, logger)) { try { await protocolHead.StartAsync(); await Task.WhenAny(cts.Token.WhenCanceled(), renewal.Token.WhenCanceled()); } catch (Exception ex) { logger.LogError($"Error starting protocol heads: {ex.Message}"); } logger.LogInformation("Stopping the protocol heads..."); await protocolHead.CloseAsync(CancellationToken.None); logger.LogInformation("Protocol heads stopped."); await CloseDbStoreProviderAsync(container); } completed.Set(); handler.ForEach(h => GC.KeepAlive(h)); logger.LogInformation("Shutdown complete."); return(0); }