using System; using Emby.Server.Implementations.Configuration; using MediaBrowser.Common.Configuration; using MediaBrowser.Controller.MediaEncoding; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using StackExchange.Redis; namespace Jellyfin.Server.Extensions; /// /// Extensions for registering the shared-configuration invalidation bus. /// public static class ConfigurationInvalidationServiceCollectionExtensions { /// /// Registers the invalidation bus, Redis-backed when a connection string is configured and no-op /// otherwise, and the subscriber applying the notices other instances publish. /// /// /// The connection string is only set for a multi-instance deployment, which is the only shape where /// one instance can write the shared configuration directory behind another's back. /// /// The service collection. /// The configuration to read the Redis connection string from. /// The logger to report the selected bus on. /// The updated service collection. public static IServiceCollection AddConfigurationInvalidationBus( this IServiceCollection serviceCollection, IConfiguration configuration, ILogger logger) { ArgumentNullException.ThrowIfNull(configuration); ArgumentNullException.ThrowIfNull(logger); if (string.IsNullOrEmpty(configuration[TranscodeStoreOptions.RedisConnectionStringKey])) { logger.LogInformation( "Configuration invalidation bus: {Bus}. Shared-configuration and library-visibility changes stay local to the instance that made them; set {Key} to propagate them.", nameof(NullConfigurationInvalidationBus), TranscodeStoreOptions.RedisConnectionStringKey); return serviceCollection.AddSingleton(NullConfigurationInvalidationBus.Instance); } logger.LogInformation( "Configuration invalidation bus: {Bus}. Shared-configuration and library-visibility changes propagate to every instance.", nameof(RedisConfigurationInvalidationBus)); serviceCollection.AddSingleton(sp => { try { return new RedisConfigurationInvalidationBus( sp.GetRequiredService(), sp.GetRequiredService>()); } catch (Exception ex) { // Fail open: an unreachable Redis degrades to the single-instance behaviour of every // instance keeping its own cached configuration, rather than aborting startup. sp.GetRequiredService>().LogError( ex, "Redis is configured but unavailable, so shared-configuration changes will not propagate between instances. Check {Key}.", TranscodeStoreOptions.RedisConnectionStringKey); return NullConfigurationInvalidationBus.Instance; } }); return serviceCollection.AddHostedService(); } }