using System.Collections.Concurrent; using Shouldly; using ZB.MOM.NatsNet.Server; namespace ZB.MOM.NatsNet.Server.Tests.ImplBacklog; public sealed partial class ConcurrencyTests2 { [Fact] // T:2491 public void NoRaceFileStoreMsgLoadNextMsgMultiPerf_ShouldSucceed() { WithStore((fs, _) => { for (var i = 0; i < 150; i++) fs.StoreMsg($"ln.{i % 6}", null, "x"u8.ToArray(), 0); var errors = new ConcurrentQueue(); Parallel.For(0, 400, _ => { try { var (sm, _) = fs.LoadNextMsgMulti(new[] { "ln.1", "ln.*" }, 1, null); if (sm != null) sm.Subject.ShouldStartWith("ln."); } catch (Exception ex) { errors.Enqueue(ex); } }); errors.ShouldBeEmpty(); fs.State().Msgs.ShouldBeGreaterThan(0UL); }); } [Fact] // T:2501 public void NoRaceFileStoreMsgLimitsAndOldRecoverState_ShouldSucceed() { var root = NewRoot(); Directory.CreateDirectory(root); try { var cfg = DefaultStreamConfig(maxMsgs: 60); var fs1 = JetStreamFileStore.NewFileStore(new FileStoreConfig { StoreDir = root }, cfg); Parallel.For(0, 180, i => fs1.StoreMsg($"lm.{i % 4}", null, "x"u8.ToArray(), 0)); fs1.Stop(); var fs2 = JetStreamFileStore.NewFileStore(new FileStoreConfig { StoreDir = root }, cfg); var (seq, _) = fs2.StoreMsg("lm.tail", null, "tail"u8.ToArray(), 0); seq.ShouldBeGreaterThan(0UL); fs2.State().Msgs.ShouldBeLessThanOrEqualTo((ulong)cfg.MaxMsgs); fs2.Stop(); } finally { Directory.Delete(root, recursive: true); } } 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(long maxMsgs = -1) { return new StreamConfig { Name = "TEST", Storage = StorageType.FileStorage, Subjects = ["test.>"], MaxMsgs = maxMsgs, MaxBytes = -1, MaxAge = TimeSpan.Zero, MaxMsgsPer = -1, Discard = DiscardPolicy.DiscardOld, Retention = RetentionPolicy.LimitsPolicy, }; } private static string NewRoot() => Path.Combine(Path.GetTempPath(), $"impl-fs-c2-{Guid.NewGuid():N}"); }