00d0765152
Publish every session to valkey with the instance holding it, and route a command for a non-local session to its owner over a per-instance pub/sub channel. Entries expire, so a dead instance leaves the directory.
67 lines
2.1 KiB
C#
67 lines
2.1 KiB
C#
using System;
|
|
using System.Threading.Tasks;
|
|
using StackExchange.Redis;
|
|
using Testcontainers.Redis;
|
|
|
|
namespace Jellyfin.Server.Tests.HighAvailability;
|
|
|
|
/// <summary>
|
|
/// Hands out a valkey/Redis server for the tests that need one. A server named by
|
|
/// <c>JELLYFIN_TEST_REDIS</c> is used as is, so CI can run one in the step instead of a docker daemon
|
|
/// of its own; without it a container is started through testcontainers.
|
|
/// </summary>
|
|
public sealed class RedisTestServer : IAsyncDisposable
|
|
{
|
|
/// <summary>
|
|
/// The connection string of an already running server.
|
|
/// </summary>
|
|
public const string ConnectionStringVariable = "JELLYFIN_TEST_REDIS";
|
|
|
|
private readonly RedisContainer? _container;
|
|
|
|
private RedisTestServer(RedisContainer? container, string connectionString)
|
|
{
|
|
_container = container;
|
|
ConnectionString = connectionString;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the connection string of the running server.
|
|
/// </summary>
|
|
public string ConnectionString { get; }
|
|
|
|
/// <summary>
|
|
/// Starts or attaches to a server and connects to it.
|
|
/// </summary>
|
|
/// <returns>The running server.</returns>
|
|
public static async Task<RedisTestServer> StartAsync()
|
|
{
|
|
var provided = Environment.GetEnvironmentVariable(ConnectionStringVariable);
|
|
if (!string.IsNullOrWhiteSpace(provided))
|
|
{
|
|
return new RedisTestServer(null, provided);
|
|
}
|
|
|
|
var container = new RedisBuilder("valkey/valkey:8-alpine").Build();
|
|
await container.StartAsync().ConfigureAwait(false);
|
|
|
|
return new RedisTestServer(container, container.GetConnectionString());
|
|
}
|
|
|
|
/// <summary>
|
|
/// Opens a connection to the server.
|
|
/// </summary>
|
|
/// <returns>The connection.</returns>
|
|
public async Task<IConnectionMultiplexer> ConnectAsync()
|
|
=> await ConnectionMultiplexer.ConnectAsync(ConnectionString).ConfigureAwait(false);
|
|
|
|
/// <inheritdoc/>
|
|
public async ValueTask DisposeAsync()
|
|
{
|
|
if (_container is not null)
|
|
{
|
|
await _container.DisposeAsync().ConfigureAwait(false);
|
|
}
|
|
}
|
|
}
|