private void UpdateRuntimeBackends(DynamicConfigRoot config) { var desiredBackends = new HashSet <string>(StringComparer.Ordinal); foreach (var configBackendPair in config.Backends) { var configBackend = configBackendPair.Value; desiredBackends.Add(configBackendPair.Key); _backendManager.GetOrCreateItem( itemId: configBackendPair.Key, setupAction: backend => { UpdateRuntimeEndpoints(configBackend.Endpoints, backend.EndpointManager); var newConfig = new BackendConfig( new BackendConfig.BackendHealthCheckOptions( enabled: configBackend.HealthCheckOptions?.Enabled ?? false, interval: configBackend.HealthCheckOptions?.Interval ?? TimeSpan.FromSeconds(0), timeout: configBackend.HealthCheckOptions?.Timeout ?? TimeSpan.FromSeconds(0), port: configBackend.HealthCheckOptions?.Port ?? 0, path: configBackend.HealthCheckOptions?.Path ?? string.Empty), new BackendConfig.BackendLoadBalancingOptions( mode: configBackend.LoadBalancing?.Mode ?? default)); var currentBackendConfig = backend.Config.Value; if (currentBackendConfig == null || currentBackendConfig.HealthCheckOptions.Enabled != newConfig.HealthCheckOptions.Enabled || currentBackendConfig.HealthCheckOptions.Interval != newConfig.HealthCheckOptions.Interval || currentBackendConfig.HealthCheckOptions.Timeout != newConfig.HealthCheckOptions.Timeout || currentBackendConfig.HealthCheckOptions.Port != newConfig.HealthCheckOptions.Port || currentBackendConfig.HealthCheckOptions.Path != newConfig.HealthCheckOptions.Path) { if (currentBackendConfig == null) { _logger.LogDebug("Backend {backendId} has been added.", configBackendPair.Key); } else { _logger.LogDebug("Backend {backendId} has changed.", configBackendPair.Key); } // Config changed, so update runtime backend backend.Config.Value = newConfig; } }); } foreach (var existingBackend in _backendManager.GetItems()) { if (!desiredBackends.Contains(existingBackend.BackendId)) { // NOTE 1: This is safe to do within the `foreach` loop // because `IBackendManager.GetItems` returns a copy of the list of backends. // // NOTE 2: Removing the backend from `IBackendManager` is safe and existing // ASP .NET Core endpoints will continue to work with their existing behavior (until those endpoints are updated) // and the Garbage Collector won't destroy this backend object while it's referenced elsewhere. _logger.LogDebug("Backend {backendId} has been removed.", existingBackend.BackendId); _backendManager.TryRemoveItem(existingBackend.BackendId); } } }