From d99427a6cf0649594102f0fc15006ab1323ff839 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stef=C3=A1n=20J=C3=B6kull=20Sigur=C3=B0arson?= Date: Sun, 25 May 2025 22:25:06 +0000 Subject: [PATCH 01/11] Adding a NATS Backplane Make Backplane Tests easily extensible Improve build process to test NATS and Redis backplanes --- .github/workflows/ci.yml | 68 ++++++-- ZiggyCreatures.FusionCache.slnx | 5 +- .../NatsBackplane.cs | 148 ++++++++++++++++++ ...reatures.FusionCache.Backplane.NATS.csproj | 25 +++ .../artwork/logo-128x128.png | Bin 0 -> 5286 bytes .../docs/README.md | 13 ++ .../RedisBackplane.cs | 1 + .../Backplane/BackplaneMessage.cs | 1 + .../Backplane/BackplaneAccessor_Async.cs | 2 + .../Backplane/BackplaneAccessor_Sync.cs | 2 + .../Internals/EncodingExtensions.cs | 42 +++++ tests/AOTTester/AOTTester.csproj | 2 +- .../L1BackplaneTests.cs | 85 +++++++--- .../L1BackplaneTests_Async.cs | 2 +- .../L1BackplaneTests_Sync.cs | 2 +- .../L1L2BackplaneTests.cs | 110 +++++++++---- .../L1L2BackplaneTests_Async.cs | 2 +- .../L1L2BackplaneTests_Sync.cs | 2 +- .../ZiggyCreatures.FusionCache.Tests.csproj | 1 + 19 files changed, 444 insertions(+), 69 deletions(-) create mode 100644 src/ZiggyCreatures.FusionCache.Backplane.NATS/NatsBackplane.cs create mode 100644 src/ZiggyCreatures.FusionCache.Backplane.NATS/ZiggyCreatures.FusionCache.Backplane.NATS.csproj create mode 100644 src/ZiggyCreatures.FusionCache.Backplane.NATS/artwork/logo-128x128.png create mode 100644 src/ZiggyCreatures.FusionCache.Backplane.NATS/docs/README.md create mode 100644 src/ZiggyCreatures.FusionCache/Internals/EncodingExtensions.cs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f3540c50..b37b5763 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -10,23 +10,73 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@master - - name: Set up .NET Core - uses: actions/setup-dotnet@v4 + - name: Build solution + run: dotnet build + - name: Run core tests + run: dotnet test --no-build --verbosity normal -l trx --results-directory '${{ env.TEST_RESULTS }}' --filter "FullyQualifiedName~FusionCache&FullyQualifiedName!~NatsL1&FullyQualifiedName!~RedisL1" + - name: Publish test results + uses: EnricoMi/publish-unit-test-result-action/linux@v2 + if: ${{ success() || failure() }} with: - dotnet-version: '9.0.x' + files: '${{ env.TEST_RESULTS }}/*.trx' + check_name: "Core Test results" + report_individual_runs: true + action_fail: true + time_unit: milliseconds + #ignore_runs: true + compare_to_earlier_commit: false - - run: dotnet --info - - name: Build solution and run all tests - run: dotnet test --verbosity normal -l trx --results-directory '${{ env.TEST_RESULTS }}' + test-nats: + runs-on: ubuntu-latest + needs: build + services: + nats: + image: nats:latest + ports: + - 4222:4222 + - 8222:8222 + redis: + image: redis:latest + ports: + - 6379:6379 + steps: + - uses: actions/checkout@master + - name: Build solution + run: dotnet build + - name: Run NatsL1 tests + run: dotnet test --no-build --verbosity normal -l trx --results-directory '${{ env.TEST_RESULTS }}' --filter "FullyQualifiedName~NatsL1" + - name: Publish Nats test results + uses: EnricoMi/publish-unit-test-result-action/linux@v2 + if: ${{ success() || failure() }} + with: + files: '${{ env.TEST_RESULTS }}/*.trx' + check_name: "Nats Test results" + report_individual_runs: true + action_fail: true + time_unit: milliseconds + compare_to_earlier_commit: false - - name: Publish test results + test-redis: + runs-on: ubuntu-latest + needs: build + services: + redis: + image: redis:latest + ports: + - 6379:6379 + steps: + - uses: actions/checkout@master + - name: Build solution + run: dotnet build + - name: Run RedisL1 tests + run: dotnet test --no-build --verbosity normal -l trx --results-directory '${{ env.TEST_RESULTS }}' --filter "FullyQualifiedName~RedisL1" + - name: Publish Redis test results uses: EnricoMi/publish-unit-test-result-action/linux@v2 if: ${{ success() || failure() }} with: files: '${{ env.TEST_RESULTS }}/*.trx' - check_name: "Test results" + check_name: "Redis Test results" report_individual_runs: true action_fail: true time_unit: milliseconds - #ignore_runs: true compare_to_earlier_commit: false diff --git a/ZiggyCreatures.FusionCache.slnx b/ZiggyCreatures.FusionCache.slnx index 03c80580..140e1e06 100644 --- a/ZiggyCreatures.FusionCache.slnx +++ b/ZiggyCreatures.FusionCache.slnx @@ -9,11 +9,12 @@ + - - + + diff --git a/src/ZiggyCreatures.FusionCache.Backplane.NATS/NatsBackplane.cs b/src/ZiggyCreatures.FusionCache.Backplane.NATS/NatsBackplane.cs new file mode 100644 index 00000000..0d63163f --- /dev/null +++ b/src/ZiggyCreatures.FusionCache.Backplane.NATS/NatsBackplane.cs @@ -0,0 +1,148 @@ +using System.Buffers; +using System.Text.Json; + +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; +using Microsoft.Extensions.Options; + +using NATS.Client.Core; + +using ZiggyCreatures.Caching.Fusion.Internals; + +namespace ZiggyCreatures.Caching.Fusion.Backplane.NATS; + +/// +/// A Redis based implementation of a FusionCache backplane. +/// +public partial class NatsBackplane + : IFusionCacheBackplane +{ + private BackplaneSubscriptionOptions? _subscriptionOptions; + private readonly ILogger? _logger; + private INatsConnection _connection; + private string _channelName = ""; + private Func? _incomingMessageHandlerAsync; + private INatsSub>? _subscription; + + /// + /// Initializes a new instance of the RedisBackplane class. + /// + /// The NATS connection instance to use. + /// The instance to use. If null, logging will be completely disabled. + public NatsBackplane(INatsConnection? natsConnection, ILogger? logger = null) + { + _connection = natsConnection ?? throw new ArgumentNullException(nameof(natsConnection)); + + // LOGGING + if (logger is NullLogger) + { + // IGNORE NULL LOGGER (FOR BETTER PERF) + _logger = null; + } + else + { + _logger = logger; + } + } + + /// + public async ValueTask SubscribeAsync(BackplaneSubscriptionOptions subscriptionOptions) + { + if (subscriptionOptions is null) + throw new ArgumentNullException(nameof(subscriptionOptions)); + + if (subscriptionOptions.ChannelName is null) + throw new NullReferenceException("The BackplaneSubscriptionOptions.ChannelName cannot be null"); + + if (subscriptionOptions.IncomingMessageHandler is null) + throw new NullReferenceException("The BackplaneSubscriptionOptions.IncomingMessageHandler cannot be null"); + + if (subscriptionOptions.ConnectHandler is null) + throw new NullReferenceException("The BackplaneSubscriptionOptions.ConnectHandler cannot be null"); + + if (subscriptionOptions.IncomingMessageHandlerAsync is null) + throw new NullReferenceException("The BackplaneSubscriptionOptions.IncomingMessageHandlerAsync cannot be null"); + + if (subscriptionOptions.ConnectHandlerAsync is null) + throw new NullReferenceException("The BackplaneSubscriptionOptions.ConnectHandlerAsync cannot be null"); + + _subscriptionOptions = subscriptionOptions; + + _channelName = _subscriptionOptions.ChannelName; + if (string.IsNullOrEmpty(_channelName)) + throw new NullReferenceException("The backplane channel name must have a value"); + + _incomingMessageHandlerAsync = _subscriptionOptions.IncomingMessageHandlerAsync; + _subscription = await _connection.SubscribeCoreAsync>(_channelName); + _ = Task.Run(async () => + { + while (await _subscription.Msgs.WaitToReadAsync().ConfigureAwait(false)) + { + while (_subscription.Msgs.TryRead(out var msg)) + { + using (msg.Data) + { + var message = BackplaneMessage.FromByteArray(msg.Data.Memory.ToArray()); + await OnMessageAsync(message).ConfigureAwait(false); + } + } + } + }); + } + + + /// + public void Subscribe(BackplaneSubscriptionOptions options) + { +#pragma warning disable VSTHRD002 // Suppressing since this is a sync-over-async method intentionally as the library doesn't provide sync APIs + SubscribeAsync(options).AsTask().Wait(); +#pragma warning restore VSTHRD002 // Avoid problematic synchronous waits + } + + /// + public async ValueTask UnsubscribeAsync() + { + if (_subscription is not null) + { + await _subscription.UnsubscribeAsync().ConfigureAwait(false); + await _subscription.Msgs.Completion; + } + } + + /// + public void Unsubscribe() + { +#pragma warning disable VSTHRD002 // Suppressing since this is a sync-over-async method intentionally as the library doesn't provide sync APIs + UnsubscribeAsync().AsTask().Wait(); +#pragma warning restore VSTHRD002 // Avoid problematic synchronous waits + } + + /// + public async ValueTask PublishAsync(BackplaneMessage message, FusionCacheEntryOptions options, CancellationToken token = default) + { + var writer = new NatsBufferWriter(); + writer.Write(BackplaneMessage.ToByteArray(message)); + await _connection.PublishAsync(_channelName, writer).ConfigureAwait(false); + } + + /// + public void Publish(BackplaneMessage message, FusionCacheEntryOptions options, CancellationToken token = default) + { +#pragma warning disable VSTHRD002 // Suppressing since this is a sync-over-async method intentionally as the library doesn't provide sync APIs + PublishAsync(message, options, token).AsTask().Wait(); +#pragma warning restore VSTHRD002 // Avoid problematic synchronous waits + } + + internal async ValueTask OnMessageAsync(BackplaneMessage message) + { + var tmp = _incomingMessageHandlerAsync; + if (tmp is null) + { + if (_logger?.IsEnabled(LogLevel.Trace) ?? false) + _logger.Log(LogLevel.Trace, "FUSION [N={CacheName} I={CacheInstanceId}]: [BP] incoming message handler was null", _subscriptionOptions?.CacheName, _subscriptionOptions?.CacheInstanceId); + return; + } + + await tmp(message).ConfigureAwait(false); + } +} diff --git a/src/ZiggyCreatures.FusionCache.Backplane.NATS/ZiggyCreatures.FusionCache.Backplane.NATS.csproj b/src/ZiggyCreatures.FusionCache.Backplane.NATS/ZiggyCreatures.FusionCache.Backplane.NATS.csproj new file mode 100644 index 00000000..a1cfad0d --- /dev/null +++ b/src/ZiggyCreatures.FusionCache.Backplane.NATS/ZiggyCreatures.FusionCache.Backplane.NATS.csproj @@ -0,0 +1,25 @@ + + + netstandard2.0;net8.0;net9.0 + 2.5.0 + ZiggyCreatures.FusionCache.Backplane.NATS + FusionCache backplane for NATS based on the NATS.Net library + backplane;nats;synadia;caching;cache;hybrid;hybrid-cache;hybridcache;multi-level;multilevel;fusion;fusioncache;fusion-cache;performance;async;ziggy + ZiggyCreatures.Caching.Fusion.Backplane.NATS + true + 1.0.0 + + + + + + + + + + + + + + + diff --git a/src/ZiggyCreatures.FusionCache.Backplane.NATS/artwork/logo-128x128.png b/src/ZiggyCreatures.FusionCache.Backplane.NATS/artwork/logo-128x128.png new file mode 100644 index 0000000000000000000000000000000000000000..ce400a79d7afa412829bf756fd31598f30250c2a GIT binary patch literal 5286 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 T encoding, string s, Span span) where T : Encoding + { + int byteCount = encoding.GetByteCount(s); + byte[] stringBytes = ArrayPool.Shared.Rent(byteCount); + try + { + encoding.GetBytes(s, 0, s.Length, stringBytes, 0); + stringBytes.AsSpan(0, byteCount).CopyTo(span); + return byteCount; + } + finally + { + ArrayPool.Shared.Return(stringBytes); + } + } + + public static string GetString(this T encoding, ReadOnlySpan bytes) where T : Encoding + { + byte[] stringBytes = ArrayPool.Shared.Rent(bytes.Length); + try + { + bytes.CopyTo(stringBytes); + return encoding.GetString(stringBytes, 0, bytes.Length); + } + finally + { + ArrayPool.Shared.Return(stringBytes); + } + } +#endif + } +} diff --git a/tests/AOTTester/AOTTester.csproj b/tests/AOTTester/AOTTester.csproj index 7ba3f559..4f8660f0 100644 --- a/tests/AOTTester/AOTTester.csproj +++ b/tests/AOTTester/AOTTester.csproj @@ -2,7 +2,7 @@ Exe - net10.0 + net9.0 enable enable true diff --git a/tests/ZiggyCreatures.FusionCache.Tests/L1BackplaneTests.cs b/tests/ZiggyCreatures.FusionCache.Tests/L1BackplaneTests.cs index b30e0ad8..e52fd4f1 100644 --- a/tests/ZiggyCreatures.FusionCache.Tests/L1BackplaneTests.cs +++ b/tests/ZiggyCreatures.FusionCache.Tests/L1BackplaneTests.cs @@ -1,26 +1,29 @@ using FusionCacheTests.Stuff; using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.Logging; + +using NATS.Client.Core; + using Xunit; using ZiggyCreatures.Caching.Fusion; using ZiggyCreatures.Caching.Fusion.Backplane; using ZiggyCreatures.Caching.Fusion.Backplane.Memory; +using ZiggyCreatures.Caching.Fusion.Backplane.NATS; using ZiggyCreatures.Caching.Fusion.Backplane.StackExchangeRedis; using ZiggyCreatures.Caching.Fusion.DangerZone; namespace FusionCacheTests; -public partial class L1BackplaneTests - : AbstractTests +public abstract partial class L1BackplaneTests : AbstractTests where T : IFusionCacheBackplane { - public L1BackplaneTests(ITestOutputHelper output) - : base(output, "MyCache:") - { - if (UseRedis) - InitialBackplaneDelay = TimeSpan.FromSeconds(5).PlusALittleBit(); + protected TimeSpan InitialBackplaneDelay = TimeSpan.FromMilliseconds(300); + protected TimeSpan MultiNodeOperationsDelay = TimeSpan.FromMilliseconds(300); + + protected L1BackplaneTests(ITestOutputHelper output) : base(output, "MyCache:") + { } - private FusionCacheOptions CreateFusionCacheOptions() + protected virtual FusionCacheOptions CreateFusionCacheOptions() { var res = new FusionCacheOptions { @@ -32,21 +35,9 @@ private FusionCacheOptions CreateFusionCacheOptions() return res; } - private static readonly bool UseRedis = false; - private static readonly string RedisConnection = "127.0.0.1:6379,ssl=False,abortConnect=false,connectTimeout=1000,syncTimeout=1000"; - - private readonly TimeSpan InitialBackplaneDelay = TimeSpan.FromMilliseconds(300); - private readonly TimeSpan MultiNodeOperationsDelay = TimeSpan.FromMilliseconds(300); - - private IFusionCacheBackplane CreateBackplane(string connectionId, ILogger? logger = null) - { - if (UseRedis) - return new RedisBackplane(new RedisBackplaneOptions { Configuration = RedisConnection }, logger: (logger as ILogger) ?? CreateXUnitLogger()); - - return new MemoryBackplane(new MemoryBackplaneOptions() { ConnectionId = connectionId }, logger: (logger as ILogger) ?? CreateXUnitLogger()); - } + protected abstract T CreateBackplane(string connectionId, ILogger? logger = null); - private FusionCache CreateFusionCache(string? cacheName, IFusionCacheBackplane? backplane, Action? setupAction = null, IMemoryCache? memoryCache = null, string? cacheInstanceId = null, ILogger? logger = null) + protected FusionCache CreateFusionCache(string? cacheName, T? backplane, Action? setupAction = null, IMemoryCache? memoryCache = null, string? cacheInstanceId = null, ILogger? logger = null) { var options = CreateFusionCacheOptions(); @@ -67,3 +58,53 @@ private FusionCache CreateFusionCache(string? cacheName, IFusionCacheBackplane? return fusionCache; } } + +public class RedisL1BackplaneTests : L1BackplaneTests +{ + private static readonly string RedisConnection = "127.0.0.1:6379,ssl=False,abortConnect=false,connectTimeout=1000,syncTimeout=1000"; + + public RedisL1BackplaneTests(ITestOutputHelper output) : base(output) + { + InitialBackplaneDelay = TimeSpan.FromSeconds(1).PlusALittleBit(); + } + + protected override RedisBackplane CreateBackplane(string connectionId, ILogger? logger = null) + { + return new RedisBackplane(new RedisBackplaneOptions { Configuration = RedisConnection }, logger: (logger as ILogger) ?? CreateXUnitLogger()); + } +} + +public class MemoryL1BackplaneTests : L1BackplaneTests +{ + public MemoryL1BackplaneTests(ITestOutputHelper output) : base(output) + { + } + + protected override MemoryBackplane CreateBackplane(string connectionId, ILogger? logger = null) + { + return new MemoryBackplane(new MemoryBackplaneOptions() { ConnectionId = connectionId }, logger: (logger as ILogger) ?? CreateXUnitLogger()); + } +} + +public class NatsL1BackplaneTests : L1BackplaneTests +{ + private static readonly string NatsConnection = "nats://localhost:4222"; + + public NatsL1BackplaneTests(ITestOutputHelper output) : base(output) + { + InitialBackplaneDelay = TimeSpan.FromSeconds(1).PlusALittleBit(); + } + + protected override FusionCacheOptions CreateFusionCacheOptions() + { + var options = base.CreateFusionCacheOptions(); + options.InternalStrings.SetToSafeStrings(); + return options; + } + + protected override NatsBackplane CreateBackplane(string connectionId, ILogger? logger = null) + { + var natsConnection = new NatsConnection(new NatsOpts() { Url = NatsConnection }); + return new NatsBackplane(natsConnection, logger: (logger as ILogger) ?? CreateXUnitLogger()); + } +} diff --git a/tests/ZiggyCreatures.FusionCache.Tests/L1BackplaneTests_Async.cs b/tests/ZiggyCreatures.FusionCache.Tests/L1BackplaneTests_Async.cs index 2b9130ef..82885c82 100644 --- a/tests/ZiggyCreatures.FusionCache.Tests/L1BackplaneTests_Async.cs +++ b/tests/ZiggyCreatures.FusionCache.Tests/L1BackplaneTests_Async.cs @@ -8,7 +8,7 @@ namespace FusionCacheTests; -public partial class L1BackplaneTests +public partial class L1BackplaneTests { [Fact] public async Task WorksWithDifferentCachesAsync() diff --git a/tests/ZiggyCreatures.FusionCache.Tests/L1BackplaneTests_Sync.cs b/tests/ZiggyCreatures.FusionCache.Tests/L1BackplaneTests_Sync.cs index 01015744..542ff0d7 100644 --- a/tests/ZiggyCreatures.FusionCache.Tests/L1BackplaneTests_Sync.cs +++ b/tests/ZiggyCreatures.FusionCache.Tests/L1BackplaneTests_Sync.cs @@ -8,7 +8,7 @@ namespace FusionCacheTests; -public partial class L1BackplaneTests +public partial class L1BackplaneTests { [Fact] public void WorksWithDifferentCaches() diff --git a/tests/ZiggyCreatures.FusionCache.Tests/L1L2BackplaneTests.cs b/tests/ZiggyCreatures.FusionCache.Tests/L1L2BackplaneTests.cs index 265e6f40..f98fbc8e 100644 --- a/tests/ZiggyCreatures.FusionCache.Tests/L1L2BackplaneTests.cs +++ b/tests/ZiggyCreatures.FusionCache.Tests/L1L2BackplaneTests.cs @@ -3,26 +3,29 @@ using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.Caching.StackExchangeRedis; using Microsoft.Extensions.Options; + +using NATS.Client.Core; + using Xunit; using ZiggyCreatures.Caching.Fusion; using ZiggyCreatures.Caching.Fusion.Backplane; using ZiggyCreatures.Caching.Fusion.Backplane.Memory; +using ZiggyCreatures.Caching.Fusion.Backplane.NATS; using ZiggyCreatures.Caching.Fusion.Backplane.StackExchangeRedis; using ZiggyCreatures.Caching.Fusion.DangerZone; namespace FusionCacheTests; -public partial class L1L2BackplaneTests - : AbstractTests +public abstract partial class L1L2BackplaneTests : AbstractTests where TBackplane : class, IFusionCacheBackplane where TDistributedCache : IDistributedCache { - public L1L2BackplaneTests(ITestOutputHelper output) - : base(output, "MyCache:") + protected TimeSpan InitialBackplaneDelay = TimeSpan.FromMilliseconds(300); + protected TimeSpan MultiNodeOperationsDelay = TimeSpan.FromMilliseconds(300); + + protected L1L2BackplaneTests(ITestOutputHelper output) : base(output, "MyCache:") { - if (UseRedis) - InitialBackplaneDelay = TimeSpan.FromSeconds(5).PlusALittleBit(); } - private FusionCacheOptions CreateFusionCacheOptions() + protected virtual FusionCacheOptions CreateFusionCacheOptions() { var res = new FusionCacheOptions { @@ -33,30 +36,11 @@ private FusionCacheOptions CreateFusionCacheOptions() return res; } - - private static readonly bool UseRedis = false; - private static readonly string RedisConnection = "127.0.0.1:6379,ssl=False,abortConnect=false,connectTimeout=1000,syncTimeout=1000"; - - private readonly TimeSpan InitialBackplaneDelay = TimeSpan.FromMilliseconds(300); - private readonly TimeSpan MultiNodeOperationsDelay = TimeSpan.FromMilliseconds(300); - - private IFusionCacheBackplane CreateBackplane(string connectionId) - { - if (UseRedis) - return new RedisBackplane(new RedisBackplaneOptions { Configuration = RedisConnection }, logger: CreateXUnitLogger()); - - return new MemoryBackplane(new MemoryBackplaneOptions() { ConnectionId = connectionId }, logger: CreateXUnitLogger()); - } - - private static IDistributedCache CreateDistributedCache() - { - if (UseRedis) - return new RedisCache(new RedisCacheOptions { Configuration = RedisConnection }); - - return new MemoryDistributedCache(Options.Create(new MemoryDistributedCacheOptions())); - } - - private FusionCache CreateFusionCache(string? cacheName, SerializerType? serializerType, IDistributedCache? distributedCache, IFusionCacheBackplane? backplane, Action? setupAction = null, IMemoryCache? memoryCache = null, string? cacheInstanceId = null) + + protected abstract TBackplane CreateBackplane(string connectionId); + protected abstract TDistributedCache CreateDistributedCache(); + + protected FusionCache CreateFusionCache(string? cacheName, SerializerType? serializerType, TDistributedCache? distributedCache, TBackplane? backplane, Action? setupAction = null, IMemoryCache? memoryCache = null, string? cacheInstanceId = null) { var options = CreateFusionCacheOptions(); @@ -83,3 +67,67 @@ private FusionCache CreateFusionCache(string? cacheName, SerializerType? seriali return fusionCache; } } + +public class RedisL1L2BackplaneTests : L1L2BackplaneTests +{ + private static readonly string RedisConnection = "127.0.0.1:6379,ssl=False,abortConnect=false,connectTimeout=1000,syncTimeout=1000"; + + public RedisL1L2BackplaneTests(ITestOutputHelper output) : base(output) + { + InitialBackplaneDelay = TimeSpan.FromSeconds(1).PlusALittleBit(); + } + + protected override RedisBackplane CreateBackplane(string connectionId) + { + return new RedisBackplane(new RedisBackplaneOptions { Configuration = RedisConnection }, logger: CreateXUnitLogger()); + } + + protected override RedisCache CreateDistributedCache() + { + return new RedisCache(new RedisCacheOptions { Configuration = RedisConnection }); + } +} + +public class MemoryL1L2BackplaneTests : L1L2BackplaneTests +{ + public MemoryL1L2BackplaneTests(ITestOutputHelper output) : base(output) + { + } + protected override MemoryBackplane CreateBackplane(string connectionId) + { + return new MemoryBackplane(new MemoryBackplaneOptions() { ConnectionId = connectionId }, logger: CreateXUnitLogger()); + } + protected override MemoryDistributedCache CreateDistributedCache() + { + return new MemoryDistributedCache(Options.Create(new MemoryDistributedCacheOptions())); + } +} + +public class NatsL1L2BackplaneTests : L1L2BackplaneTests +{ + private static readonly string NatsConnection = "nats://localhost:4222"; + private static readonly string RedisConnection = "127.0.0.1:6379,ssl=False,abortConnect=false,connectTimeout=1000,syncTimeout=1000"; + + public NatsL1L2BackplaneTests(ITestOutputHelper output) : base(output) + { + InitialBackplaneDelay = TimeSpan.FromSeconds(1).PlusALittleBit(); + } + + protected override FusionCacheOptions CreateFusionCacheOptions() + { + var options = base.CreateFusionCacheOptions(); + options.InternalStrings.SetToSafeStrings(); + return options; + } + + protected override NatsBackplane CreateBackplane(string connectionId) + { + var natsConnection = new NatsConnection(new NatsOpts() { Url = NatsConnection }); + return new NatsBackplane(natsConnection, logger: CreateXUnitLogger()); + } + + protected override RedisCache CreateDistributedCache() + { + return new RedisCache(new RedisCacheOptions { Configuration = RedisConnection }); + } +} diff --git a/tests/ZiggyCreatures.FusionCache.Tests/L1L2BackplaneTests_Async.cs b/tests/ZiggyCreatures.FusionCache.Tests/L1L2BackplaneTests_Async.cs index 16e9d25f..e293dc9e 100644 --- a/tests/ZiggyCreatures.FusionCache.Tests/L1L2BackplaneTests_Async.cs +++ b/tests/ZiggyCreatures.FusionCache.Tests/L1L2BackplaneTests_Async.cs @@ -14,7 +14,7 @@ namespace FusionCacheTests; -public partial class L1L2BackplaneTests +public partial class L1L2BackplaneTests { [Theory] [ClassData(typeof(SerializerTypesClassData))] diff --git a/tests/ZiggyCreatures.FusionCache.Tests/L1L2BackplaneTests_Sync.cs b/tests/ZiggyCreatures.FusionCache.Tests/L1L2BackplaneTests_Sync.cs index 7e9bee11..4e8ffafe 100644 --- a/tests/ZiggyCreatures.FusionCache.Tests/L1L2BackplaneTests_Sync.cs +++ b/tests/ZiggyCreatures.FusionCache.Tests/L1L2BackplaneTests_Sync.cs @@ -14,7 +14,7 @@ namespace FusionCacheTests; -public partial class L1L2BackplaneTests +public partial class L1L2BackplaneTests { [Theory] [ClassData(typeof(SerializerTypesClassData))] diff --git a/tests/ZiggyCreatures.FusionCache.Tests/ZiggyCreatures.FusionCache.Tests.csproj b/tests/ZiggyCreatures.FusionCache.Tests/ZiggyCreatures.FusionCache.Tests.csproj index 153ded50..4635a2ee 100644 --- a/tests/ZiggyCreatures.FusionCache.Tests/ZiggyCreatures.FusionCache.Tests.csproj +++ b/tests/ZiggyCreatures.FusionCache.Tests/ZiggyCreatures.FusionCache.Tests.csproj @@ -35,6 +35,7 @@ + From e75c2a3f26f163f0c0b55fe73d69c84edf04186f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stef=C3=A1n=20J=C3=B6kull=20Sigur=C3=B0arson?= Date: Fri, 28 Aug 2026 08:28:09 +0000 Subject: [PATCH 02/11] Updating after recent merges from main --- global.json | 3 +++ .../NatsBackplane.cs | 4 ++-- .../ZiggyCreatures.FusionCache.Backplane.NATS.csproj | 2 +- src/ZiggyCreatures.FusionCache.Backplane.NATS/docs/README.md | 2 +- tests/AOTTester/AOTTester.csproj | 2 +- 5 files changed, 8 insertions(+), 5 deletions(-) diff --git a/global.json b/global.json index 70afea0b..6aceed38 100644 --- a/global.json +++ b/global.json @@ -3,5 +3,8 @@ "version": "10.0.100", "allowPrerelease": true, "rollForward": "latestFeature" + }, + "test": { + "runner": "Microsoft.Testing.Platform" } } \ No newline at end of file diff --git a/src/ZiggyCreatures.FusionCache.Backplane.NATS/NatsBackplane.cs b/src/ZiggyCreatures.FusionCache.Backplane.NATS/NatsBackplane.cs index 0d63163f..912f3946 100644 --- a/src/ZiggyCreatures.FusionCache.Backplane.NATS/NatsBackplane.cs +++ b/src/ZiggyCreatures.FusionCache.Backplane.NATS/NatsBackplane.cs @@ -12,7 +12,7 @@ namespace ZiggyCreatures.Caching.Fusion.Backplane.NATS; /// -/// A Redis based implementation of a FusionCache backplane. +/// A NATS based implementation of a FusionCache backplane. /// public partial class NatsBackplane : IFusionCacheBackplane @@ -25,7 +25,7 @@ public partial class NatsBackplane private INatsSub>? _subscription; /// - /// Initializes a new instance of the RedisBackplane class. + /// Initializes a new instance of the NatsBackplane class. /// /// The NATS connection instance to use. /// The instance to use. If null, logging will be completely disabled. diff --git a/src/ZiggyCreatures.FusionCache.Backplane.NATS/ZiggyCreatures.FusionCache.Backplane.NATS.csproj b/src/ZiggyCreatures.FusionCache.Backplane.NATS/ZiggyCreatures.FusionCache.Backplane.NATS.csproj index a1cfad0d..39246d1c 100644 --- a/src/ZiggyCreatures.FusionCache.Backplane.NATS/ZiggyCreatures.FusionCache.Backplane.NATS.csproj +++ b/src/ZiggyCreatures.FusionCache.Backplane.NATS/ZiggyCreatures.FusionCache.Backplane.NATS.csproj @@ -16,7 +16,7 @@ - + diff --git a/src/ZiggyCreatures.FusionCache.Backplane.NATS/docs/README.md b/src/ZiggyCreatures.FusionCache.Backplane.NATS/docs/README.md index e529a7ba..bb11c1c2 100644 --- a/src/ZiggyCreatures.FusionCache.Backplane.NATS/docs/README.md +++ b/src/ZiggyCreatures.FusionCache.Backplane.NATS/docs/README.md @@ -10,4 +10,4 @@ Find out [more](https://github.com/ZiggyCreatures/FusionCache). ## 📦 This package -This package is a backplane implementation on [NATS](https://nats.io/) based on the awesome [StackExchange.Redis](https://github.com/StackExchange/StackExchange.Redis) library. \ No newline at end of file +This package is a backplane implementation on [NATS](https://nats.io/) based on the [NATS.Net](https://github.com/nats-io/nats.net) library. \ No newline at end of file diff --git a/tests/AOTTester/AOTTester.csproj b/tests/AOTTester/AOTTester.csproj index 4f8660f0..7ba3f559 100644 --- a/tests/AOTTester/AOTTester.csproj +++ b/tests/AOTTester/AOTTester.csproj @@ -2,7 +2,7 @@ Exe - net9.0 + net10.0 enable enable true From 25136e13a694a501655ac76a5d660bb030a388d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stef=C3=A1n=20J=C3=B6kull=20Sigur=C3=B0arson?= Date: Fri, 28 Aug 2026 08:44:11 +0000 Subject: [PATCH 03/11] Updating NATS package and fixing test filter --- .github/workflows/ci.yml | 6 +++--- .../ZiggyCreatures.FusionCache.Backplane.NATS.csproj | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 906a0bff..9c7f5f0c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,7 +17,7 @@ jobs: - name: Build solution run: dotnet build - name: Run core tests - run: dotnet test --no-build --verbosity normal -l trx --results-directory '${{ env.TEST_RESULTS }}' --filter "FullyQualifiedName~FusionCache&FullyQualifiedName!~NatsL1&FullyQualifiedName!~RedisL1" + run: dotnet test --no-build --verbosity normal -l trx --results-directory '${{ env.TEST_RESULTS }}' --filter-query '/*/*/(!*NatsL1*)&(!*RedisL1*)' - name: Publish test results uses: EnricoMi/publish-unit-test-result-action/linux@v2 if: ${{ success() || failure() }} @@ -52,7 +52,7 @@ jobs: - name: Build solution run: dotnet build - name: Run NatsL1 tests - run: dotnet test --no-build --verbosity normal -l trx --results-directory '${{ env.TEST_RESULTS }}' --filter "FullyQualifiedName~NatsL1" + run: dotnet test --no-build --verbosity normal -l trx --results-directory '${{ env.TEST_RESULTS }}' --filter-query '/*/*/*NatsL1*' - name: Publish Nats test results uses: EnricoMi/publish-unit-test-result-action/linux@v2 if: ${{ success() || failure() }} @@ -81,7 +81,7 @@ jobs: - name: Build solution run: dotnet build - name: Run RedisL1 tests - run: dotnet test --no-build --verbosity normal -l trx --results-directory '${{ env.TEST_RESULTS }}' --filter "FullyQualifiedName~RedisL1" + run: dotnet test --no-build --verbosity normal -l trx --results-directory '${{ env.TEST_RESULTS }}' --filter-query '/*/*/*RedisL1*' - name: Publish Redis test results uses: EnricoMi/publish-unit-test-result-action/linux@v2 if: ${{ success() || failure() }} diff --git a/src/ZiggyCreatures.FusionCache.Backplane.NATS/ZiggyCreatures.FusionCache.Backplane.NATS.csproj b/src/ZiggyCreatures.FusionCache.Backplane.NATS/ZiggyCreatures.FusionCache.Backplane.NATS.csproj index 39246d1c..709ba400 100644 --- a/src/ZiggyCreatures.FusionCache.Backplane.NATS/ZiggyCreatures.FusionCache.Backplane.NATS.csproj +++ b/src/ZiggyCreatures.FusionCache.Backplane.NATS/ZiggyCreatures.FusionCache.Backplane.NATS.csproj @@ -16,7 +16,7 @@ - + From c23e48a5ce57e3a34ee41884947a393d62205d3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stef=C3=A1n=20J=C3=B6kull=20Sigur=C3=B0arson?= Date: Fri, 28 Aug 2026 08:50:55 +0000 Subject: [PATCH 04/11] Fixing test runner options --- .github/workflows/ci.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9c7f5f0c..bf71bcaa 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,7 +17,7 @@ jobs: - name: Build solution run: dotnet build - name: Run core tests - run: dotnet test --no-build --verbosity normal -l trx --results-directory '${{ env.TEST_RESULTS }}' --filter-query '/*/*/(!*NatsL1*)&(!*RedisL1*)' + run: dotnet test --no-build --verbosity normal -- --results-directory '${{ env.TEST_RESULTS }}' --report-xunit-trx --filter-query '/*/*/(!*NatsL1*)&(!*RedisL1*)' - name: Publish test results uses: EnricoMi/publish-unit-test-result-action/linux@v2 if: ${{ success() || failure() }} @@ -52,7 +52,7 @@ jobs: - name: Build solution run: dotnet build - name: Run NatsL1 tests - run: dotnet test --no-build --verbosity normal -l trx --results-directory '${{ env.TEST_RESULTS }}' --filter-query '/*/*/*NatsL1*' + run: dotnet test --no-build --verbosity normal -- --results-directory '${{ env.TEST_RESULTS }}' --report-xunit-trx --filter-query '/*/*/*NatsL1*' - name: Publish Nats test results uses: EnricoMi/publish-unit-test-result-action/linux@v2 if: ${{ success() || failure() }} @@ -81,7 +81,7 @@ jobs: - name: Build solution run: dotnet build - name: Run RedisL1 tests - run: dotnet test --no-build --verbosity normal -l trx --results-directory '${{ env.TEST_RESULTS }}' --filter-query '/*/*/*RedisL1*' + run: dotnet test --no-build --verbosity normal -- --results-directory '${{ env.TEST_RESULTS }}' --report-xunit-trx --filter-query '/*/*/*RedisL1*' - name: Publish Redis test results uses: EnricoMi/publish-unit-test-result-action/linux@v2 if: ${{ success() || failure() }} From 4cb19c123a5785dde9dfaff85b82f00ad29d4e4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stef=C3=A1n=20J=C3=B6kull=20Sigur=C3=B0arson?= Date: Fri, 28 Aug 2026 09:10:52 +0000 Subject: [PATCH 05/11] Simplifying the backplane test runs to use matrixes --- .github/workflows/ci.yml | 49 ++++++++++++---------------------------- 1 file changed, 14 insertions(+), 35 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bf71bcaa..2505af6b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -30,9 +30,17 @@ jobs: #ignore_runs: true compare_to_earlier_commit: false - test-nats: + test-backplanes: + name: ${{ matrix.name }} L1 tests runs-on: ubuntu-latest - needs: build + strategy: + fail-fast: false + matrix: + include: + - name: Nats + filter: '/*/*/*NatsL1*' + - name: Redis + filter: '/*/*/*RedisL1*' services: nats: image: nats:latest @@ -51,43 +59,14 @@ jobs: global-json-file: global.json - name: Build solution run: dotnet build - - name: Run NatsL1 tests - run: dotnet test --no-build --verbosity normal -- --results-directory '${{ env.TEST_RESULTS }}' --report-xunit-trx --filter-query '/*/*/*NatsL1*' - - name: Publish Nats test results + - name: Run ${{ matrix.name }}L1 tests + run: dotnet test --no-build --verbosity normal -- --results-directory '${{ env.TEST_RESULTS }}' --report-xunit-trx --filter-query '${{ matrix.filter }}' + - name: Publish ${{ matrix.name }} test results uses: EnricoMi/publish-unit-test-result-action/linux@v2 if: ${{ success() || failure() }} with: files: '${{ env.TEST_RESULTS }}/*.trx' - check_name: "Nats Test results" - report_individual_runs: true - action_fail: true - time_unit: milliseconds - compare_to_earlier_commit: false - - test-redis: - runs-on: ubuntu-latest - needs: build - services: - redis: - image: redis:latest - ports: - - 6379:6379 - steps: - - uses: actions/checkout@master - - name: Set up .NET Core - uses: actions/setup-dotnet@v5 - with: - global-json-file: global.json - - name: Build solution - run: dotnet build - - name: Run RedisL1 tests - run: dotnet test --no-build --verbosity normal -- --results-directory '${{ env.TEST_RESULTS }}' --report-xunit-trx --filter-query '/*/*/*RedisL1*' - - name: Publish Redis test results - uses: EnricoMi/publish-unit-test-result-action/linux@v2 - if: ${{ success() || failure() }} - with: - files: '${{ env.TEST_RESULTS }}/*.trx' - check_name: "Redis Test results" + check_name: "${{ matrix.name }} Test results" report_individual_runs: true action_fail: true time_unit: milliseconds From 9917befe0caae27b47a1f594c5c2bdaedcaa1974 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stef=C3=A1n=20J=C3=B6kull=20Sigur=C3=B0arson?= Date: Fri, 28 Aug 2026 13:28:33 +0000 Subject: [PATCH 06/11] Publish test reports for troubleshooting --- .github/workflows/ci.yml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2505af6b..459df9a6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -29,6 +29,13 @@ jobs: time_unit: milliseconds #ignore_runs: true compare_to_earlier_commit: false + - name: Upload core test reports + uses: actions/upload-artifact@v4 + if: ${{ always() }} + with: + name: core-test-reports + path: '${{ env.TEST_RESULTS }}/*.trx' + if-no-files-found: warn test-backplanes: name: ${{ matrix.name }} L1 tests @@ -71,3 +78,10 @@ jobs: action_fail: true time_unit: milliseconds compare_to_earlier_commit: false + - name: Upload ${{ matrix.name }} test reports + uses: actions/upload-artifact@v4 + if: ${{ always() }} + with: + name: ${{ matrix.name }}-test-reports + path: '${{ env.TEST_RESULTS }}/*.trx' + if-no-files-found: warn From 16a38972419cc12dc939f8d6bf1d3b863959f487 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stef=C3=A1n=20J=C3=B6kull=20Sigur=C3=B0arson?= Date: Fri, 28 Aug 2026 13:29:35 +0000 Subject: [PATCH 07/11] Updating actions --- .github/workflows/ci.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 459df9a6..2769fcca 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,7 +11,7 @@ jobs: steps: - uses: actions/checkout@master - name: Set up .NET Core - uses: actions/setup-dotnet@v5 + uses: actions/setup-dotnet@v6 with: global-json-file: global.json - name: Build solution @@ -30,7 +30,7 @@ jobs: #ignore_runs: true compare_to_earlier_commit: false - name: Upload core test reports - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 if: ${{ always() }} with: name: core-test-reports @@ -61,7 +61,7 @@ jobs: steps: - uses: actions/checkout@master - name: Set up .NET Core - uses: actions/setup-dotnet@v5 + uses: actions/setup-dotnet@v6 with: global-json-file: global.json - name: Build solution @@ -79,7 +79,7 @@ jobs: time_unit: milliseconds compare_to_earlier_commit: false - name: Upload ${{ matrix.name }} test reports - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 if: ${{ always() }} with: name: ${{ matrix.name }}-test-reports From 58183ee8a3d0e99d4426f67a720404486b51706b Mon Sep 17 00:00:00 2001 From: Jody Donetti Date: Wed, 2 Sep 2026 21:11:17 +0200 Subject: [PATCH 08/11] Removing useless newlines which create extra diffs --- .../RedisBackplane.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/ZiggyCreatures.FusionCache.Backplane.StackExchangeRedis/RedisBackplane.cs b/src/ZiggyCreatures.FusionCache.Backplane.StackExchangeRedis/RedisBackplane.cs index 934909d8..b133caf4 100644 --- a/src/ZiggyCreatures.FusionCache.Backplane.StackExchangeRedis/RedisBackplane.cs +++ b/src/ZiggyCreatures.FusionCache.Backplane.StackExchangeRedis/RedisBackplane.cs @@ -1,7 +1,6 @@ using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Options; - using StackExchange.Redis; namespace ZiggyCreatures.Caching.Fusion.Backplane.StackExchangeRedis; From bf6663b657d1bd4c95bfdf82f5ac85435c73bfc9 Mon Sep 17 00:00:00 2001 From: Jody Donetti Date: Wed, 2 Sep 2026 21:12:06 +0200 Subject: [PATCH 09/11] Removing useless newlines which create extra diffs --- src/ZiggyCreatures.FusionCache/Backplane/BackplaneMessage.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/ZiggyCreatures.FusionCache/Backplane/BackplaneMessage.cs b/src/ZiggyCreatures.FusionCache/Backplane/BackplaneMessage.cs index 0a6a0369..efdcb824 100644 --- a/src/ZiggyCreatures.FusionCache/Backplane/BackplaneMessage.cs +++ b/src/ZiggyCreatures.FusionCache/Backplane/BackplaneMessage.cs @@ -1,6 +1,5 @@ using System.Buffers.Binary; using System.Text; - using ZiggyCreatures.Caching.Fusion.Internals; namespace ZiggyCreatures.Caching.Fusion.Backplane; From a9b6ba0cb5ee36b53da7828e412fe86e0eab8568 Mon Sep 17 00:00:00 2001 From: Jody Donetti Date: Wed, 2 Sep 2026 21:12:25 +0200 Subject: [PATCH 10/11] Update BackplaneAccessor_Async.cs --- .../Internals/Backplane/BackplaneAccessor_Async.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/ZiggyCreatures.FusionCache/Internals/Backplane/BackplaneAccessor_Async.cs b/src/ZiggyCreatures.FusionCache/Internals/Backplane/BackplaneAccessor_Async.cs index 703b5ddf..96656a0f 100644 --- a/src/ZiggyCreatures.FusionCache/Internals/Backplane/BackplaneAccessor_Async.cs +++ b/src/ZiggyCreatures.FusionCache/Internals/Backplane/BackplaneAccessor_Async.cs @@ -1,7 +1,5 @@ using System.Diagnostics; - using Microsoft.Extensions.Logging; - using ZiggyCreatures.Caching.Fusion.Backplane; using ZiggyCreatures.Caching.Fusion.Internals.Diagnostics; From be659b5470b9d088fdbd9d3414ad267d7b9f4e1e Mon Sep 17 00:00:00 2001 From: Jody Donetti Date: Wed, 2 Sep 2026 21:12:46 +0200 Subject: [PATCH 11/11] Update BackplaneAccessor_Sync.cs --- .../Internals/Backplane/BackplaneAccessor_Sync.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/ZiggyCreatures.FusionCache/Internals/Backplane/BackplaneAccessor_Sync.cs b/src/ZiggyCreatures.FusionCache/Internals/Backplane/BackplaneAccessor_Sync.cs index bf9d54a5..79b60ad1 100644 --- a/src/ZiggyCreatures.FusionCache/Internals/Backplane/BackplaneAccessor_Sync.cs +++ b/src/ZiggyCreatures.FusionCache/Internals/Backplane/BackplaneAccessor_Sync.cs @@ -1,7 +1,5 @@ using System.Diagnostics; - using Microsoft.Extensions.Logging; - using ZiggyCreatures.Caching.Fusion.Backplane; using ZiggyCreatures.Caching.Fusion.Internals.Diagnostics;