using System.Collections.Concurrent; using Shouldly; using ZB.MOM.NatsNet.Server; namespace ZB.MOM.NatsNet.Server.Tests.ImplBacklog; public sealed partial class ConcurrencyTests1 { [Fact] // T:2452 public void NoRaceFileStoreStreamMaxAgePerformance_ShouldSucceed() { WithStore((fs, _) => { Parallel.For(0, 200, i => fs.StoreMsg($"age.{i % 4}", null, new[] { (byte)(i % 255) }, 0)); var state = fs.State(); state.Msgs.ShouldBeGreaterThan(0UL); state.LastSeq.ShouldBeGreaterThanOrEqualTo(state.Msgs); var (total, validThrough, err) = fs.NumPending(1, ">", false); err.ShouldBeNull(); total.ShouldBeGreaterThan(0UL); validThrough.ShouldBeGreaterThan(0UL); }, DefaultStreamConfig(maxAge: TimeSpan.FromMilliseconds(20))); } [Fact] // T:2453 public void NoRaceFileStoreFilteredStateWithLargeDeletes_ShouldSucceed() { WithStore((fs, _) => { for (var i = 0; i < 240; i++) fs.StoreMsg("fd", null, new[] { (byte)(i % 255) }, 0); Parallel.For(1L, 240L, i => { if (i % 3 == 0) fs.RemoveMsg((ulong)i); }); var filtered = fs.FilteredState(1, "fd"); filtered.Msgs.ShouldBeGreaterThan(0UL); filtered.Last.ShouldBeGreaterThanOrEqualTo(filtered.First); fs.SubjectsTotals(">")["fd"].ShouldBeGreaterThan(0UL); }); } [Fact] // T:2462 public void NoRaceFileStoreNumPending_ShouldSucceed() { WithStore((fs, _) => { for (var i = 0; i < 100; i++) fs.StoreMsg($"np.{i % 5}", null, "x"u8.ToArray(), 0); var errors = new ConcurrentQueue(); var workers = Enumerable.Range(0, 8).Select(_ => Task.Run(() => { try { for (var i = 0; i < 40; i++) { var (_, _, err1) = fs.NumPending(1, ">", false); if (err1 != null) throw err1; var (_, _, err2) = fs.NumPendingMulti(1, new[] { "np.1", "np.*" }, false); if (err2 != null) throw err2; } } catch (Exception ex) { errors.Enqueue(ex); } })).ToArray(); Task.WaitAll(workers); errors.ShouldBeEmpty(); }); } private static void WithStore(Action action, StreamConfig? cfg = null) { var root = NewRoot(); Directory.CreateDirectory(root); JetStreamFileStore? fs = null; try { fs = JetStreamFileStore.NewFileStore(new FileStoreConfig { StoreDir = root }, cfg ?? DefaultStreamConfig()); action(fs, root); } finally { fs?.Stop(); if (Directory.Exists(root)) Directory.Delete(root, recursive: true); } } private static StreamConfig DefaultStreamConfig(TimeSpan? maxAge = null) { return new StreamConfig { Name = "TEST", Storage = StorageType.FileStorage, Subjects = ["test.>"], MaxMsgs = -1, MaxBytes = -1, MaxAge = maxAge ?? TimeSpan.Zero, MaxMsgsPer = -1, Discard = DiscardPolicy.DiscardOld, Retention = RetentionPolicy.LimitsPolicy, }; } private static string NewRoot() => Path.Combine(Path.GetTempPath(), $"impl-fs-c1-{Guid.NewGuid():N}"); }