Files
unkin-agent b662ffa48f
ci/woodpecker/push/ci Pipeline was successful
ci/woodpecker/pr/ci Pipeline was successful
propagate shared-config and library-option changes between instances
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.
2026-09-20 23:47:21 +10:00

74 lines
2.2 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using MediaBrowser.Common.Configuration;
namespace Jellyfin.Server.Implementations.Tests.Configuration;
/// <summary>
/// An in-process stand-in for the Redis pub/sub bus: every endpoint connected to one fabric receives
/// what the others publish, and never its own notices.
/// </summary>
internal sealed class FakeInvalidationBusFabric
{
private readonly List<Endpoint> _endpoints = new();
/// <summary>
/// Connects a new instance to the fabric.
/// </summary>
/// <param name="originId">The identity of the connecting instance.</param>
/// <returns>The bus of that instance.</returns>
public IConfigurationInvalidationBus Connect(string originId)
{
var endpoint = new Endpoint(this, originId);
lock (_endpoints)
{
_endpoints.Add(endpoint);
}
return endpoint;
}
private void Broadcast(ConfigurationInvalidation invalidation)
{
Endpoint[] endpoints;
lock (_endpoints)
{
endpoints = _endpoints.ToArray();
}
foreach (var endpoint in endpoints.Where(e => !string.Equals(e.OriginId, invalidation.OriginId, StringComparison.Ordinal)))
{
endpoint.Deliver(invalidation);
}
}
private sealed class Endpoint : IConfigurationInvalidationBus
{
private readonly FakeInvalidationBusFabric _fabric;
private readonly List<Action<ConfigurationInvalidation>> _handlers = new();
public Endpoint(FakeInvalidationBusFabric fabric, string originId)
{
_fabric = fabric;
OriginId = originId;
}
public string OriginId { get; }
public void Publish(ConfigurationInvalidationScope scope, string? target)
=> _fabric.Broadcast(new ConfigurationInvalidation { Scope = scope, Target = target, OriginId = OriginId });
public void Subscribe(Action<ConfigurationInvalidation> handler)
=> _handlers.Add(handler);
public void Deliver(ConfigurationInvalidation invalidation)
{
foreach (var handler in _handlers)
{
handler(invalidation);
}
}
}
}