propagate shared-config and library-option changes between instances
ci/woodpecker/push/ci Pipeline was successful
ci/woodpecker/pr/ci Pipeline was successful

Add a Redis pub/sub invalidation bus, publish from the configuration and library-option write paths, and drop the matching local cache entry on the instances that did not write. No-op without a Redis connection string, and fails open when it is unreachable.
This commit is contained in:
2026-09-20 23:47:21 +10:00
parent ec581b5e5f
commit b662ffa48f
16 changed files with 919 additions and 1 deletions
@@ -14,6 +14,7 @@ using System.Threading.Tasks;
using Jellyfin.Data.Enums;
using Jellyfin.Database.Implementations.Entities;
using Jellyfin.Extensions.Json;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Controller.IO;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers;
@@ -70,6 +71,12 @@ namespace MediaBrowser.Controller.Entities
public static IServerApplicationHost ApplicationHost { get; set; }
/// <summary>
/// Gets or sets the bus announcing library option writes to the other instances sharing this
/// configuration directory. Defaults to a no-op, which is the single-instance behaviour.
/// </summary>
public static IConfigurationInvalidationBus InvalidationBus { get; set; } = NullConfigurationInvalidationBus.Instance;
[JsonIgnore]
public override bool SupportsPlayedStatus => false;
@@ -188,11 +195,36 @@ namespace MediaBrowser.Controller.Entities
XmlSerializer.SerializeToFile(clone, GetLibraryOptionsPath(path));
LibraryOptionsUpdated?.Invoke(null, new LibraryOptionsUpdatedEventArgs(path, options));
InvalidationBus.Publish(ConfigurationInvalidationScope.LibraryOptions, path);
}
public static void OnCollectionFolderChange()
/// <summary>
/// Drops the cached options of one library so the next read comes off <c>options.xml</c> again.
/// Applied on the instances that did not write, and so does not publish.
/// </summary>
/// <param name="path">The library path.</param>
public static void InvalidateLibraryOptions(string path)
{
_libraryOptions.TryRemove(path, out _);
LibraryOptionsUpdated?.Invoke(null, new LibraryOptionsUpdatedEventArgs(path, GetLibraryOptions(path)));
}
/// <summary>
/// Drops every cached library option set. Applied on the instances that did not write, and so does
/// not publish.
/// </summary>
public static void InvalidateAllLibraryOptions()
=> _libraryOptions.Clear();
public static void OnCollectionFolderChange()
{
InvalidateAllLibraryOptions();
InvalidationBus.Publish(ConfigurationInvalidationScope.AllLibraryOptions, null);
}
public override bool IsSaveLocalMetadataEnabled()
{
return true;