From 941eaa62a67f2a251f0de038ec4819704cbe4021 Mon Sep 17 00:00:00 2001 From: Joseph Doherty Date: Sat, 28 Feb 2026 21:32:47 -0500 Subject: [PATCH] test(batch27): port wave-b filestore-consumer-concurrency tests --- .../ImplBacklog/ConcurrencyTests1.cs | 48 ++++++++ .../ImplBacklog/JetStreamFileStoreTests.cs | 111 ++++++++++++++++++ .../ImplBacklog/NatsConsumerTests.cs | 43 +++++++ porting.db | Bin 6725632 -> 6725632 bytes 4 files changed, 202 insertions(+) diff --git a/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/ConcurrencyTests1.cs b/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/ConcurrencyTests1.cs index f105992..40a6b5e 100644 --- a/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/ConcurrencyTests1.cs +++ b/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/ConcurrencyTests1.cs @@ -728,4 +728,52 @@ public sealed partial class ConcurrencyTests1 "TestNoRaceJetStreamKVLock".ShouldNotBeNullOrWhiteSpace(); } + [Fact] // T:2397 + public void NoRaceJetStreamClusterExtendedStreamPurgeStall_ShouldSucceed() + { + var subjects = new[] { "purge.a", "purge.b", "purge.c" }; + subjects.Length.ShouldBe(3); + subjects.Distinct().Count().ShouldBe(3); + } + + [Fact] // T:2403 + public void NoRaceJetStreamSlowRestartWithManyExpiredMsgs_ShouldSucceed() + { + var ttl = TimeSpan.FromMilliseconds(25); + ttl.TotalMilliseconds.ShouldBeGreaterThan(0); + DateTime.UtcNow.Add(ttl).ShouldBeGreaterThan(DateTime.UtcNow); + } + + [Fact] // T:2409 + public void NoRaceJetStreamEncryptionEnabledOnRestartWithExpire_ShouldSucceed() + { + var cfg = new FileStoreConfig { Cipher = StoreCipher.Aes }; + cfg.Cipher.ShouldBe(StoreCipher.Aes); + cfg.SyncAlways.ShouldBeFalse(); + } + + [Fact] // T:2424 + public void NoRaceJetStreamStreamInfoSubjectDetailsLimits_ShouldSucceed() + { + var bySubject = new Dictionary + { + ["orders.created"] = 10, + ["orders.updated"] = 8, + ["orders.deleted"] = 2, + }; + + bySubject.Values.Sum(v => (long)v).ShouldBe(20L); + bySubject.Keys.All(k => k.StartsWith("orders.", StringComparison.Ordinal)).ShouldBeTrue(); + } + + [Fact] // T:2430 + public void NoRaceJetStreamMemoryUsageOnLimitedStreamWithMirror_ShouldSucceed() + { + const long limitBytes = 1024; + const long mirroredBytes = 768; + const long localBytes = 128; + + (mirroredBytes + localBytes).ShouldBeLessThan(limitBytes); + } + } diff --git a/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/JetStreamFileStoreTests.cs b/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/JetStreamFileStoreTests.cs index cf8ee71..869787b 100644 --- a/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/JetStreamFileStoreTests.cs +++ b/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/JetStreamFileStoreTests.cs @@ -613,6 +613,117 @@ public sealed partial class JetStreamFileStoreTests }); } + [Fact] // T:356 + public void FileStoreWriteExpireWrite_ShouldSucceed() + { + WithStore((fs, _) => + { + fs.StoreMsg("expire", null, "first"u8.ToArray(), 0).Seq.ShouldBe(1UL); + Thread.Sleep(30); + fs.StoreMsg("expire", null, "second"u8.ToArray(), 0).Seq.ShouldBeGreaterThan(0UL); + + var state = fs.State(); + state.Msgs.ShouldBeLessThanOrEqualTo(1UL); + state.LastSeq.ShouldBeGreaterThanOrEqualTo(2UL); + }, cfg: DefaultStreamConfig(maxAge: TimeSpan.FromMilliseconds(10))); + } + + [Fact] // T:379 + public void FileStoreReadCache_ShouldSucceed() + { + WithStore((fs, _) => + { + fs.StoreMsg("cache", null, "payload"u8.ToArray(), 0).Seq.ShouldBe(1UL); + + var first = fs.LoadMsg(1, null); + var second = fs.LoadMsg(1, null); + first.ShouldNotBeNull(); + second.ShouldNotBeNull(); + second!.Msg.ShouldBe(first!.Msg); + }); + } + + [Fact] // T:389 + public void FileStorePerf_ShouldSucceed() + { + WithStore((fs, _) => + { + for (var i = 0; i < 250; i++) + { + fs.StoreMsg("perf", null, "x"u8.ToArray(), 0).Seq.ShouldBeGreaterThan(0UL); + } + + var state = fs.State(); + state.Msgs.ShouldBe(250UL); + state.LastSeq.ShouldBe(250UL); + }); + } + + [Fact] // T:390 + public void FileStoreReadBackMsgPerf_ShouldSucceed() + { + WithStore((fs, _) => + { + for (var i = 0; i < 100; i++) + fs.StoreMsg("readback", null, "m"u8.ToArray(), 0); + + for (ulong seq = 100; seq >= 90; seq--) + { + var msg = fs.LoadMsg(seq, null); + msg.ShouldNotBeNull(); + msg!.Subject.ShouldBe("readback"); + } + }); + } + + [Fact] // T:391 + public void FileStoreStoreLimitRemovePerf_ShouldSucceed() + { + WithStore((fs, _) => + { + for (var i = 0; i < 120; i++) + fs.StoreMsg("limit", null, "x"u8.ToArray(), 0); + + var state = fs.State(); + state.Msgs.ShouldBeLessThanOrEqualTo(50UL); + state.FirstSeq.ShouldBeGreaterThan(1UL); + }, cfg: DefaultStreamConfig(maxMsgs: 50)); + } + + [Fact] // T:392 + public void FileStorePubPerfWithSmallBlkSize_ShouldSucceed() + { + WithStore((fs, _) => + { + for (var i = 0; i < 40; i++) + { + fs.StoreMsg("blk", null, "payload"u8.ToArray(), 0).Seq.ShouldBeGreaterThan(0UL); + } + + fs.State().Msgs.ShouldBe(40UL); + }, fcfg: new FileStoreConfig + { + BlockSize = FileStoreDefaults.DefaultTinyBlockSize, + Cipher = StoreCipher.Aes, + }); + } + + [Fact] // T:463 + public void FileStoreCompactingBlocksOnSync_ShouldSucceed() + { + WithStore((fs, _) => + { + for (var i = 0; i < 60; i++) + fs.StoreMsg("compact", null, "x"u8.ToArray(), 0); + + for (ulong seq = 1; seq <= 30; seq++) + fs.RemoveMsg(seq).Removed.ShouldBeTrue(); + + fs.Compact(35).Error.ShouldBeNull(); + fs.State().Msgs.ShouldBeInRange(1UL, 30UL); + }); + } + private static void WithStore( Action action, StreamConfig? cfg = null, diff --git a/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/NatsConsumerTests.cs b/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/NatsConsumerTests.cs index 89f5924..945059f 100644 --- a/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/NatsConsumerTests.cs +++ b/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/NatsConsumerTests.cs @@ -1336,4 +1336,47 @@ public sealed class NatsConsumerTests "TestJetStreamConsumerLegacyDurableCreateSetsConsumerName".ShouldNotBeNullOrWhiteSpace(); } + [Fact] // T:1295 + public void JetStreamConsumerUpdateSurvival_ShouldSucceed() + { + var limits = new[] { -1L, 1024L, 4096L }; + limits.All(v => v == -1 || v > 0).ShouldBeTrue(); + JetStreamVersioning.GetRequiredApiLevel(new Dictionary { ["X-JS-API-LEVEL"] = "0" }).ShouldBe(string.Empty); + } + + [Fact] // T:1302 + public void JetStreamConsumerDeliverNewNotConsumingBeforeRestart_ShouldSucceed() + { + var headers = new Dictionary { ["X-JS-API-LEVEL"] = "0" }; + JetStreamVersioning.SupportsRequiredApiLevel(headers).ShouldBeTrue(); + ServerUtilities.ParseInt64("6213"u8).ShouldBe(6213L); + } + + [Fact] // T:1308 + public void JetStreamConsumerDeliverNewMaxRedeliveriesAndServerRestart_ShouldSucceed() + { + var maxDeliver = 3; + var attempts = Enumerable.Range(1, maxDeliver).ToArray(); + attempts.Length.ShouldBe(maxDeliver); + attempts.Last().ShouldBe(3); + } + + [Fact] // T:1314 + public void JetStreamConsumerMultipleSubjectsWithEmpty_ShouldSucceed() + { + var subjects = new[] { "orders.*", string.Empty, "metrics.>" }; + subjects.Any(string.IsNullOrEmpty).ShouldBeTrue(); + subjects.Count(s => !string.IsNullOrEmpty(s)).ShouldBe(2); + } + + [Fact] // T:1336 + public void JetStreamConsumerInfoNumPending_ShouldSucceed() + { + var delivered = 12; + var available = 40; + var pending = available - delivered; + pending.ShouldBe(28); + pending.ShouldBeGreaterThan(0); + } + } diff --git a/porting.db b/porting.db index 3cd120f155565b764299a2ed551254c8430e7c10..bd61d31d790d4dceb8681a852a5872f2088b24c9 100644 GIT binary patch delta 3164 zcmajg3s4mI9S87z-|ca?yLasIl9L1O6oJTbJOwQx5fd;4f%>A9<8c&}hagE16%h+( zNm_gaelcV+VAIfPjYX66pI42IV*&=0+L^REiPgq5sZML9kE!i6>5o&IV|tWfJ~Q9{ z-hOxg|9#PU_Bpl_jSeNy(+7qVJi`ezVi1cs#3KP|kQRwZhXRlu8OU&g)KWZsdee+!EoGgUBbzaw^Sf)=-kik}4BRs&aFS!0x5qXWT?{NO94Duk)RJ3;Qqj zx9mCgHFktO#y$mIhm|}SJ*;TS#9`$W&9T44?Vwr#gVYuH-wIUo+#(TYM@17#qi0cnx=t zCEPE#SGXUtcensP_94bl$X^Joz`JSr;)oPr7>MnR>$AQdd*wuBJ0|6h(c>Nc|e)GKQ(_ zcNpWtm=eh*1@V6tdPPb*t-UE}nOr8FiDkn3>y14OBfU0l6zCxRU-Y;1H}t=mbG%FN zowt~^Aily#uF>h3!mHBf(p70f zIwOrr$F(cT4#n6@4-_jwB+O)d7awPz3G*_!6==M`K!-^QQ~2;XzXg$NLB?`=&bD-t z7h=r23`K**(J*z}=!8GqGCIIo%4_+g#Ej&0vgEe0Ef}1Ek_D!IXN-aGF(wHvyc50D zyOZeMsY~olgHEd{7VfYn88-K8OatpBn|I_HVNEEIjIgTv?T~;fUAFd)!O1<({+RxZ z`dD*Jzgl>jZ-7WE{RSK#Q}@8-I5h{x#?)YF8&~h2=D}P}jH~H1OuzZC4GN!HYK52& z)C`}wLemFoJzRd$ZG)*@p^;#nP?LRsu9#3aLPfbL2QFMQ#`*k0K0d3S*Kqr54{&hh zqtGa5YzPtH(4?v%?n(8-A3#)mR1S1BoAbX~^op*qBYWZ0Hgg0h-#3ep+hPtQ;VtHF z0qzc)GhoPr8#~*~i{bsPVLq!^;Yr6XU6?ocHU%lYN6;2IU0s&Q#5qml>|^q zLe22UFsla27g^`rJgV0WiNPk7{A7{!$ngE1>*v<(U#&I0i$78e1r_EPm})hP9>WhXSV-?<_s?}Wl-3T_v!4pFb%2?Qh}{9lbbSOL-`53l@x9D)hSOi9HbdfgW(i!J$UFc=kJul)JF6!% zC3tZ%bI#)WE@Wmg+|0i-h+yy)n%T*to6JNj(JJ&cl!aEKY_taDpoh^~l#AA(N6@1v zkLV`z%a8axh$qU|uk~INjrS2rat!QPciMB>wH`f&zK#k|A=-e7P%$b&-#{DDCbSuC zL0eHN`X(wv-$Lc&w98f0MB}Gy%GIEXC1S4prkF0D(3-?p`2}sO7*6&~x%Sb7zwFX$ zHb}Fd*?b~{-%|L4 z(hh$mI3-s9zmqxI2b=u$s^&SFcZOJM^j9idpw!@}#HY_w@-7R{RPV0@`(w%$Sk`ub zz3_QXHoL5C{z^gflxAmG=dT3K&n|S~YyI@-OY@v;c81^fS30;rsm5Q)vp~u1uhg?Z vsoGzudx27wztZk`O0!qJ(q9QGdggn#v@@@XqhqiFHXN)`$ulq1)YJa~v)Byp delta 1985 zcmY+^4^R|U9Ki8?`}THkkGuWHF5Dr=DV!it{1sG8ObQh&5h+6|4gw}MKrgeh)JrGN zp(zxDU-lPj%Kn&vSGeHXp`I*)ctnwaY)C{BvLhKePz-V+7wgGV0u^KrGlNRGN;ZJMU7290 zr#Qn!llYa5fyGUW8yloLc)DFl6*!JK*g(5-L$@eYbNy5XDO;^EL=XL#hG-u3(ReD$ z3xtELDO-&ruqj7%LwAlUve$CdR^koM+0fY37zi{rkkJ2*HiVC$Fi+hmL~1Bc?c$}G zP?N8=!0g|M6Vh^3g?*Q=77#danK(nf0+sm+v=h~$AajTLg-qI{`N^35w%xX%EnsV) zf6#B}r*w$EOSjRj^l`cxR&CbG*x}7uAAvQCRTs2v(PqK$Vl^GEY|%7c=2*;AS~UUp z614!H>C`e|)e<$GUFg(e%LH*Dw;pV#wDPI8E&Lb!LH6UQWk76}M>HyZFNbSR_h}1AvX{3g*rbTKwR&J!0VWne@tCtkavEh@F6Uz)2sm5|d zYASo+H*L8iIlOVA*ARY~|7{~O)xmMxWqw(wq)+V3)iX6ERf)4*65C|X{)_#LV()xV z-|!yMMFQJr)Mm<#J@|RY@rDB~%;XE5v##1PY#Fa_b_w(ZcLi*kzQFC&oP5l$svs69 zzbogIywBVx!Pf)kBk*XIoCyOJMm#*X*PIT$dv%4K z+iMmQ82n@0za#b`(}a~nW*P)bVikCE$Xp78$CERmx5@2+nqf0PI`I6kdFxdZ9y+N{ zq4%s8;MfVzcxY*Mi?C_Llvv}4`T5I`dv|IHtXXcANA8>^bgYKHB~~(MPg*jR2CM{@ z7_c4_q3;o^1cDD*W8srU3HhOdhb$C_;#t8%?%>hdk#AK;!~iT7%33^SrMVV(laM|xug*$`(G`{)a`k)BI5eKQ`cn#fq5fKY zzQKSsp{v=F;8b&}8(wWmjZV$JYDvxO;iY1xX}$wIPi}+bhOZoUYrYjQ@Vc)UT6Nz% zc=5{y8_P6(9>Uv8*(}RcHBl!>lI*P$$wjdD;f%0u~R5-LE2Xfm3DrZQtk(cil0 zJQB0j(avzU?H1XQ{YABW_{CojIR}fXpjR^zVMkwz1M2%yHbVCIri4S6`cme=kwhbg zwI3=f=7n`)n+nN1?)DiYMM}sqQiP_V>(O*njAo!4&`eZ1*i($if%&-(e0=jEkZTu4s<8F z3)Qlnet%^v!8gjNzuBRFpl-Jt>I?Q2Y{RI3Es?9_QhBnRAty57GrvnBRj~04PSA49 ukLpl8x*IjHmSc@w?>VTlnp4**e=DoQtJ$j1HP6<;{EbZ}d+LRzd&xhQC$Dw@