using System; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Common.Configuration; using MediaBrowser.Controller.Entities; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; namespace Emby.Server.Implementations.Configuration { /// /// Applies the configuration invalidations published by the other instances sharing this /// configuration directory, dropping the local cache entry so the next read comes off the shared file. /// public sealed class ConfigurationInvalidationSubscriber : IHostedService { private readonly IConfigurationInvalidationBus _bus; private readonly IConfigurationManager _configurationManager; private readonly ILogger _logger; /// /// Initializes a new instance of the class. /// /// The invalidation bus. /// The configuration manager holding the cached configuration. /// The logger. public ConfigurationInvalidationSubscriber( IConfigurationInvalidationBus bus, IConfigurationManager configurationManager, ILogger logger) { _bus = bus; _configurationManager = configurationManager; _logger = logger; } /// public Task StartAsync(CancellationToken cancellationToken) { _bus.Subscribe(Apply); return Task.CompletedTask; } /// public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask; private void Apply(ConfigurationInvalidation invalidation) { try { // Applying re-raises the same update events a local save raises, so the in-process // consumers re-read. The scope tells those consumers that the write was somebody else's, // so the ones that answer an update by writing neither repeat it nor publish it back. using var scope = ConfigurationInvalidationContext.BeginApply(); switch (invalidation.Scope) { case ConfigurationInvalidationScope.SystemConfiguration: _configurationManager.InvalidateCachedConfiguration(null); break; case ConfigurationInvalidationScope.NamedConfiguration when !string.IsNullOrEmpty(invalidation.Target): _configurationManager.InvalidateCachedConfiguration(invalidation.Target); break; case ConfigurationInvalidationScope.LibraryOptions when !string.IsNullOrEmpty(invalidation.Target): CollectionFolder.InvalidateLibraryOptions(invalidation.Target); break; case ConfigurationInvalidationScope.AllLibraryOptions: CollectionFolder.InvalidateAllLibraryOptions(); break; default: return; } _logger.LogDebug("Applied {Scope} invalidation for {Target} from {OriginId}.", invalidation.Scope, invalidation.Target, invalidation.OriginId); } catch (Exception ex) { _logger.LogWarning(ex, "Failed to apply a {Scope} invalidation for {Target}.", invalidation.Scope, invalidation.Target); } } } }