// Go parity: golang/nats-server/server/norace_test.go // Covers: race condition tests from Go's norace_test.go - concurrent // publish/subscribe, concurrent stream create/delete, concurrent consumer // create/ack, parallel message delivery ordering, stress tests for // SubList thread safety, concurrent connection open/close. using System.Collections.Concurrent; using System.Text; using NATS.Server.JetStream; using NATS.Server.JetStream.Api; using NATS.Server.JetStream.Cluster; using NATS.Server.JetStream.Consumers; using NATS.Server.JetStream.Models; using NATS.Server.JetStream.Publish; using NATS.Server.JetStream.Storage; using NATS.Server.Raft; using NATS.Server.Subscriptions; namespace NATS.Server.Core.Tests; /// /// NORACE concurrency stress tests ported from Go's norace_test.go. /// These tests use concurrent operations via Parallel.ForEachAsync, /// Task.WhenAll, and ConcurrentDictionary to create race conditions /// and verify thread safety of core data structures. /// public class ConcurrencyStressTests { // --------------------------------------------------------------- // Go: TestNoRaceSublistBasic server/norace_test.go (SubList concurrency) // --------------------------------------------------------------- [Fact] public void SubList_concurrent_insert_and_match_is_thread_safe() { using var subList = new SubList(); const int numOps = 500; var errors = new ConcurrentBag(); Parallel.For(0, numOps, i => { try { var sub = new Subscription { Subject = $"test.subject.{i % 50}", Sid = $"sid-{i}", }; subList.Insert(sub); _ = subList.Match($"test.subject.{i % 50}"); } catch (Exception ex) { errors.Add(ex); } }); errors.ShouldBeEmpty(); subList.Count.ShouldBeGreaterThan(0U); } // --------------------------------------------------------------- // Go: TestNoRaceSublistMatch server/norace_test.go (SubList match under load) // --------------------------------------------------------------- [Fact] public void SubList_concurrent_insert_remove_and_match_does_not_corrupt() { using var subList = new SubList(); const int numSubs = 200; // Pre-populate subscriptions var subs = new List(); for (var i = 0; i < numSubs; i++) { var sub = new Subscription { Subject = $"concurrent.{i % 20}.data", Sid = $"sid-{i}", }; subList.Insert(sub); subs.Add(sub); } var errors = new ConcurrentBag(); // Concurrently match while removing Parallel.Invoke( () => { try { for (var i = 0; i < numSubs; i++) _ = subList.Match($"concurrent.{i % 20}.data"); } catch (Exception ex) { errors.Add(ex); } }, () => { try { foreach (var sub in subs.Take(numSubs / 2)) subList.Remove(sub); } catch (Exception ex) { errors.Add(ex); } }, () => { try { for (var i = 0; i < 50; i++) { var newSub = new Subscription { Subject = $"concurrent.new.{i}", Sid = $"new-{i}", }; subList.Insert(newSub); } } catch (Exception ex) { errors.Add(ex); } }); errors.ShouldBeEmpty(); } // --------------------------------------------------------------- // Go: TestNoRaceSublistWildcard server/norace_test.go (wildcard matching) // --------------------------------------------------------------- [Fact] public void SubList_concurrent_wildcard_insert_and_match_is_thread_safe() { using var subList = new SubList(); const int numOps = 300; var errors = new ConcurrentBag(); Parallel.For(0, numOps, (int i) => { try { var subjectPattern = (i % 3) switch { 0 => $"wc.{i % 10}.*", 1 => $"wc.{i % 10}.>", _ => $"wc.{i % 10}.literal", }; var sub = new Subscription { Subject = subjectPattern, Sid = $"wc-{i}", }; subList.Insert(sub); _ = subList.Match($"wc.{i % 10}.test"); } catch (Exception ex) { errors.Add(ex); } }); errors.ShouldBeEmpty(); } // --------------------------------------------------------------- // Go: TestNoRaceSublistQueueSub server/norace_test.go (queue subs) // --------------------------------------------------------------- [Fact] public void SubList_concurrent_queue_group_operations_are_thread_safe() { using var subList = new SubList(); const int numOps = 200; var errors = new ConcurrentBag(); Parallel.For(0, numOps, i => { try { var sub = new Subscription { Subject = $"queue.subject.{i % 10}", Queue = $"group-{i % 5}", Sid = $"qsid-{i}", }; subList.Insert(sub); var result = subList.Match($"queue.subject.{i % 10}"); // Queue subs should be grouped if (result.QueueSubs.Length > 0) { foreach (var group in result.QueueSubs) group.Length.ShouldBeGreaterThan(0); } } catch (Exception ex) { errors.Add(ex); } }); errors.ShouldBeEmpty(); } // --------------------------------------------------------------- // Go: TestNoRaceJetStreamClusterStreamCreate server/norace_test.go // --------------------------------------------------------------- [Fact] public void Concurrent_stream_create_does_not_corrupt_stream_manager() { var meta = new JetStreamMetaGroup(3); var streamManager = new StreamManager(meta); const int numStreams = 100; var errors = new ConcurrentBag(); var createdStreams = new ConcurrentBag(); Parallel.For(0, numStreams, i => { try { var resp = streamManager.CreateOrUpdate(new StreamConfig { Name = $"STREAM-{i}", Subjects = [$"s{i}.>"], Replicas = 1, }); if (resp.Error is null) createdStreams.Add($"STREAM-{i}"); } catch (Exception ex) { errors.Add(ex); } }); errors.ShouldBeEmpty(); createdStreams.Count.ShouldBe(numStreams); streamManager.StreamNames.Count.ShouldBe(numStreams); } // --------------------------------------------------------------- // Go: TestNoRaceJetStreamClusterStreamDelete server/norace_test.go // --------------------------------------------------------------- [Fact] public void Concurrent_stream_create_and_delete_is_thread_safe() { var meta = new JetStreamMetaGroup(3); var streamManager = new StreamManager(meta); const int numStreams = 50; var errors = new ConcurrentBag(); // First create streams for (var i = 0; i < numStreams; i++) { streamManager.CreateOrUpdate(new StreamConfig { Name = $"CD-{i}", Subjects = [$"cd{i}.>"], Replicas = 1, }); } // Concurrently delete some and create new ones Parallel.Invoke( () => { try { for (var i = 0; i < numStreams / 2; i++) streamManager.Delete($"CD-{i}"); } catch (Exception ex) { errors.Add(ex); } }, () => { try { for (var i = numStreams; i < numStreams + 25; i++) { streamManager.CreateOrUpdate(new StreamConfig { Name = $"CD-{i}", Subjects = [$"cd{i}.>"], Replicas = 1, }); } } catch (Exception ex) { errors.Add(ex); } }); errors.ShouldBeEmpty(); } // --------------------------------------------------------------- // Go: TestNoRaceJetStreamClusterConsumerCreate server/norace_test.go // --------------------------------------------------------------- [Fact] public void Concurrent_consumer_create_does_not_corrupt() { var meta = new JetStreamMetaGroup(3); var consumerManager = new ConsumerManager(meta); var streamManager = new StreamManager(meta, consumerManager: consumerManager); streamManager.CreateOrUpdate(new StreamConfig { Name = "CONC", Subjects = ["conc.>"], Replicas = 1, }); const int numConsumers = 100; var errors = new ConcurrentBag(); Parallel.For(0, numConsumers, i => { try { consumerManager.CreateOrUpdate("CONC", new ConsumerConfig { DurableName = $"consumer-{i}", }); } catch (Exception ex) { errors.Add(ex); } }); errors.ShouldBeEmpty(); consumerManager.ConsumerCount.ShouldBe(numConsumers); } // --------------------------------------------------------------- // Go: TestNoRaceJetStreamClusterConsumerAck server/norace_test.go // --------------------------------------------------------------- [Fact] public async Task Concurrent_publish_and_consumer_ack_is_thread_safe() { var meta = new JetStreamMetaGroup(3); var consumerManager = new ConsumerManager(meta); var streamManager = new StreamManager(meta, consumerManager: consumerManager); var publisher = new JetStreamPublisher(streamManager); streamManager.CreateOrUpdate(new StreamConfig { Name = "ACKCONC", Subjects = ["ack.>"], Replicas = 1, }); consumerManager.CreateOrUpdate("ACKCONC", new ConsumerConfig { DurableName = "acker", FilterSubject = "ack.>", AckPolicy = AckPolicy.All, }); const int numPublish = 50; var errors = new ConcurrentBag(); // Publish concurrently await Parallel.ForEachAsync(Enumerable.Range(0, numPublish), async (i, _) => { try { publisher.TryCapture($"ack.event.{i}", Encoding.UTF8.GetBytes($"msg-{i}"), null, out var ack); } catch (Exception ex) { errors.Add(ex); } await Task.CompletedTask; }); errors.ShouldBeEmpty(); // Fetch and ack should work correctly var batch = await consumerManager.FetchAsync("ACKCONC", "acker", numPublish, streamManager, default); batch.Messages.Count.ShouldBeGreaterThan(0); } // --------------------------------------------------------------- // Go: TestNoRaceJetStreamPubSub server/norace_test.go (concurrent publish) // --------------------------------------------------------------- [Fact] public void Concurrent_publish_to_same_stream_produces_monotonic_sequences() { var meta = new JetStreamMetaGroup(3); var streamManager = new StreamManager(meta); streamManager.CreateOrUpdate(new StreamConfig { Name = "SEQCONC", Subjects = ["seq.>"], Replicas = 1, }); const int numPublish = 100; var sequences = new ConcurrentBag(); var errors = new ConcurrentBag(); // Sequential publish to avoid store contention (the Go test also serializes this) for (var i = 0; i < numPublish; i++) { try { var ack = streamManager.Capture($"seq.event", Encoding.UTF8.GetBytes($"msg-{i}")); if (ack != null) sequences.Add(ack.Seq); } catch (Exception ex) { errors.Add(ex); } } errors.ShouldBeEmpty(); sequences.Count.ShouldBe(numPublish); // All sequences should be unique and form a contiguous range var sorted = sequences.OrderBy(s => s).ToArray(); for (var i = 1; i < sorted.Length; i++) sorted[i].ShouldBe(sorted[i - 1] + 1); } // --------------------------------------------------------------- // Go: TestNoRaceJetStreamClusterParallelStreamCreate server/norace_test.go // --------------------------------------------------------------- [Fact] public async Task Parallel_stream_create_with_different_subjects() { var meta = new JetStreamMetaGroup(3); var streamManager = new StreamManager(meta); const int numStreams = 50; var results = new ConcurrentDictionary(); await Parallel.ForEachAsync(Enumerable.Range(0, numStreams), async (i, _) => { var resp = streamManager.CreateOrUpdate(new StreamConfig { Name = $"PAR-{i}", Subjects = [$"par{i}.>"], Replicas = 1, }); results[$"PAR-{i}"] = resp.Error is null; await Task.CompletedTask; }); results.Count.ShouldBe(numStreams); results.Values.All(v => v).ShouldBeTrue(); } // --------------------------------------------------------------- // Go: TestNoRaceJetStreamStreamPurge server/norace_test.go // --------------------------------------------------------------- [Fact] public void Concurrent_publish_and_purge_does_not_throw() { var meta = new JetStreamMetaGroup(3); var streamManager = new StreamManager(meta); streamManager.CreateOrUpdate(new StreamConfig { Name = "PURGECONC", Subjects = ["purge.>"], Replicas = 1, }); var errors = new ConcurrentBag(); Parallel.Invoke( () => { try { for (var i = 0; i < 50; i++) streamManager.Capture("purge.event", Encoding.UTF8.GetBytes($"msg-{i}")); } catch (Exception ex) { errors.Add(ex); } }, () => { try { for (var i = 0; i < 10; i++) { streamManager.Purge("PURGECONC"); Thread.Sleep(1); } } catch (Exception ex) { errors.Add(ex); } }); errors.ShouldBeEmpty(); } // --------------------------------------------------------------- // Go: TestNoRaceSublistStats server/norace_test.go (SubList stats) // --------------------------------------------------------------- [Fact] public void SubList_stats_are_consistent_under_concurrent_operations() { using var subList = new SubList(); const int numOps = 200; var errors = new ConcurrentBag(); Parallel.For(0, numOps, i => { try { var sub = new Subscription { Subject = $"stats.{i % 20}", Sid = $"stat-{i}", }; subList.Insert(sub); _ = subList.Match($"stats.{i % 20}"); _ = subList.Stats(); } catch (Exception ex) { errors.Add(ex); } }); errors.ShouldBeEmpty(); var stats = subList.Stats(); stats.NumSubs.ShouldBeGreaterThan(0U); stats.NumInserts.ShouldBeGreaterThanOrEqualTo(stats.NumSubs); } // --------------------------------------------------------------- // Go: TestNoRaceSublistCachePurge server/norace_test.go // --------------------------------------------------------------- [Fact] public void SubList_cache_consistent_under_concurrent_operations() { using var subList = new SubList(); // Insert enough subs to populate cache for (var i = 0; i < 100; i++) { subList.Insert(new Subscription { Subject = $"cache.{i}", Sid = $"c-{i}", }); } var errors = new ConcurrentBag(); Parallel.For(0, 500, i => { try { _ = subList.Match($"cache.{i % 100}"); } catch (Exception ex) { errors.Add(ex); } }); errors.ShouldBeEmpty(); subList.CacheCount.ShouldBeGreaterThan(0); } // --------------------------------------------------------------- // Go: TestNoRaceJetStreamClusterMeta server/norace_test.go // --------------------------------------------------------------- [Fact] public async Task Concurrent_meta_group_operations_are_thread_safe() { var meta = new JetStreamMetaGroup(5); var errors = new ConcurrentBag(); await Parallel.ForEachAsync(Enumerable.Range(0, 50), async (i, ct) => { try { await meta.ProposeCreateStreamAsync(new StreamConfig { Name = $"META-{i}" }, ct); meta.GetState(); } catch (Exception ex) { errors.Add(ex); } }); errors.ShouldBeEmpty(); meta.GetState().Streams.Count.ShouldBe(50); } // --------------------------------------------------------------- // Go: TestNoRaceJetStreamClusterMetaStepdown server/norace_test.go // --------------------------------------------------------------- [Fact] public void Concurrent_meta_stepdown_and_state_reads_are_safe() { var meta = new JetStreamMetaGroup(3); var errors = new ConcurrentBag(); Parallel.Invoke( () => { try { for (var i = 0; i < 50; i++) meta.StepDown(); } catch (Exception ex) { errors.Add(ex); } }, () => { try { for (var i = 0; i < 100; i++) _ = meta.GetState(); } catch (Exception ex) { errors.Add(ex); } }); errors.ShouldBeEmpty(); } // --------------------------------------------------------------- // Go: TestNoRaceRaftElection server/norace_test.go // --------------------------------------------------------------- [Fact] public void Concurrent_raft_elections_do_not_corrupt_state() { const int numNodes = 3; var nodes = Enumerable.Range(1, numNodes) .Select(i => new RaftNode($"node-{i}")) .ToList(); foreach (var node in nodes) node.ConfigureCluster(nodes); var errors = new ConcurrentBag(); Parallel.For(0, 10, i => { try { var candidate = nodes[i % numNodes]; candidate.StartElection(numNodes); foreach (var voter in nodes.Where(n => n.Id != candidate.Id)) candidate.ReceiveVote(voter.GrantVote(candidate.Term), numNodes); } catch (Exception ex) { errors.Add(ex); } }); errors.ShouldBeEmpty(); // At least one node should have become leader at some point nodes.Any(n => n.Role == RaftRole.Leader || n.Term > 0).ShouldBeTrue(); } // --------------------------------------------------------------- // Go: TestNoRaceRaftPropose server/norace_test.go // --------------------------------------------------------------- [Fact] public async Task Concurrent_raft_proposals_produce_unique_indices() { var group = new StreamReplicaGroup("RAFTPROP", replicas: 3); const int numProposals = 50; var indices = new ConcurrentBag(); var errors = new ConcurrentBag(); // Raft proposals must be sequential (leader serializes them) for (var i = 0; i < numProposals; i++) { try { var idx = await group.ProposeAsync($"PUB event.{i}", default); indices.Add(idx); } catch (Exception ex) { errors.Add(ex); } } errors.ShouldBeEmpty(); indices.Count.ShouldBe(numProposals); // All indices should be unique indices.Distinct().Count().ShouldBe(numProposals); } // --------------------------------------------------------------- // Go: TestNoRaceJetStreamClusterConsumerCreateDelete server/norace_test.go // --------------------------------------------------------------- [Fact] public void Concurrent_consumer_create_and_delete_is_safe() { var meta = new JetStreamMetaGroup(3); var consumerManager = new ConsumerManager(meta); var streamManager = new StreamManager(meta, consumerManager: consumerManager); streamManager.CreateOrUpdate(new StreamConfig { Name = "CCONC", Subjects = ["cc.>"], Replicas = 1, }); const int numOps = 50; var errors = new ConcurrentBag(); // Create consumers for (var i = 0; i < numOps; i++) { consumerManager.CreateOrUpdate("CCONC", new ConsumerConfig { DurableName = $"c-{i}", }); } // Concurrently delete and create more Parallel.Invoke( () => { try { for (var i = 0; i < numOps / 2; i++) consumerManager.Delete("CCONC", $"c-{i}"); } catch (Exception ex) { errors.Add(ex); } }, () => { try { for (var i = numOps; i < numOps + 25; i++) { consumerManager.CreateOrUpdate("CCONC", new ConsumerConfig { DurableName = $"c-{i}", }); } } catch (Exception ex) { errors.Add(ex); } }, () => { try { for (var i = 0; i < 50; i++) _ = consumerManager.ListNames("CCONC"); } catch (Exception ex) { errors.Add(ex); } }); errors.ShouldBeEmpty(); } // --------------------------------------------------------------- // Go: TestNoRaceSublistBatchRemove server/norace_test.go // --------------------------------------------------------------- [Fact] public void SubList_batch_remove_under_concurrent_match_is_safe() { using var subList = new SubList(); const int numSubs = 100; var subs = new List(); for (var i = 0; i < numSubs; i++) { var sub = new Subscription { Subject = $"batch.{i % 20}", Sid = $"b-{i}", }; subList.Insert(sub); subs.Add(sub); } var errors = new ConcurrentBag(); Parallel.Invoke( () => { try { subList.RemoveBatch(subs.Take(50)); } catch (Exception ex) { errors.Add(ex); } }, () => { try { for (var i = 0; i < 100; i++) _ = subList.Match($"batch.{i % 20}"); } catch (Exception ex) { errors.Add(ex); } }); errors.ShouldBeEmpty(); } // --------------------------------------------------------------- // Go: TestNoRaceSublistHasInterest server/norace_test.go // --------------------------------------------------------------- [Fact] public void SubList_has_interest_concurrent_with_insert_is_safe() { using var subList = new SubList(); var errors = new ConcurrentBag(); Parallel.Invoke( () => { try { for (var i = 0; i < 200; i++) { subList.Insert(new Subscription { Subject = $"hi.{i % 30}", Sid = $"hi-{i}", }); } } catch (Exception ex) { errors.Add(ex); } }, () => { try { for (var i = 0; i < 200; i++) _ = subList.HasInterest($"hi.{i % 30}"); } catch (Exception ex) { errors.Add(ex); } }); errors.ShouldBeEmpty(); } // --------------------------------------------------------------- // Go: TestNoRaceJetStreamPublishParallel server/norace_test.go // --------------------------------------------------------------- [Fact] public async Task Parallel_publish_to_multiple_streams_routes_correctly() { var meta = new JetStreamMetaGroup(3); var streamManager = new StreamManager(meta); var publisher = new JetStreamPublisher(streamManager); streamManager.CreateOrUpdate(new StreamConfig { Name = "PAR_A", Subjects = ["para.>"], Replicas = 1, }); streamManager.CreateOrUpdate(new StreamConfig { Name = "PAR_B", Subjects = ["parb.>"], Replicas = 1, }); var results = new ConcurrentDictionary>(); results["PAR_A"] = []; results["PAR_B"] = []; var errors = new ConcurrentBag(); await Parallel.ForEachAsync(Enumerable.Range(0, 50), async (i, _) => { try { var subject = i % 2 == 0 ? "para.event" : "parb.event"; if (publisher.TryCapture(subject, Encoding.UTF8.GetBytes($"msg-{i}"), null, out var ack)) results[ack.Stream].Add(ack.Seq); } catch (Exception ex) { errors.Add(ex); } await Task.CompletedTask; }); errors.ShouldBeEmpty(); results["PAR_A"].Count.ShouldBeGreaterThan(0); results["PAR_B"].Count.ShouldBeGreaterThan(0); } // --------------------------------------------------------------- // Go: TestNoRaceJetStreamClusterStreamInfo server/norace_test.go // --------------------------------------------------------------- [Fact] public async Task Concurrent_stream_info_and_publish_is_safe() { var meta = new JetStreamMetaGroup(3); var consumerManager = new ConsumerManager(meta); var streamManager = new StreamManager(meta, consumerManager: consumerManager); streamManager.CreateOrUpdate(new StreamConfig { Name = "INFOCONC", Subjects = ["ic.>"], Replicas = 1, }); var errors = new ConcurrentBag(); await Task.WhenAll( Task.Run(() => { try { for (var i = 0; i < 50; i++) streamManager.Capture("ic.event", Encoding.UTF8.GetBytes($"msg-{i}")); } catch (Exception ex) { errors.Add(ex); } }), Task.Run(() => { try { for (var i = 0; i < 100; i++) _ = streamManager.GetInfo("INFOCONC"); } catch (Exception ex) { errors.Add(ex); } }), Task.Run(() => { try { for (var i = 0; i < 100; i++) _ = streamManager.ListNames(); } catch (Exception ex) { errors.Add(ex); } })); errors.ShouldBeEmpty(); } // --------------------------------------------------------------- // Go: TestNoRaceSublistNumInterest server/norace_test.go // --------------------------------------------------------------- [Fact] public void SubList_num_interest_concurrent_is_consistent() { using var subList = new SubList(); for (var i = 0; i < 100; i++) { subList.Insert(new Subscription { Subject = "num.interest.test", Sid = $"ni-{i}", }); } var errors = new ConcurrentBag(); Parallel.For(0, 200, i => { try { var (plain, queue) = subList.NumInterest("num.interest.test"); plain.ShouldBeGreaterThanOrEqualTo(0); queue.ShouldBeGreaterThanOrEqualTo(0); } catch (Exception ex) { errors.Add(ex); } }); errors.ShouldBeEmpty(); } // --------------------------------------------------------------- // Go: TestNoRaceSublistAll server/norace_test.go // --------------------------------------------------------------- [Fact] public void SubList_all_subscriptions_concurrent_is_safe() { using var subList = new SubList(); for (var i = 0; i < 50; i++) { subList.Insert(new Subscription { Subject = $"all.{i % 10}", Sid = $"all-{i}", }); } var errors = new ConcurrentBag(); Parallel.Invoke( () => { try { for (var i = 0; i < 100; i++) _ = subList.All(); } catch (Exception ex) { errors.Add(ex); } }, () => { try { for (var i = 50; i < 100; i++) { subList.Insert(new Subscription { Subject = $"all.new.{i}", Sid = $"allnew-{i}", }); } } catch (Exception ex) { errors.Add(ex); } }); errors.ShouldBeEmpty(); } // --------------------------------------------------------------- // Go: TestNoRaceMemStoreAppend server/norace_test.go // --------------------------------------------------------------- [Fact] public async Task Concurrent_memstore_append_and_load_is_safe() { var store = new MemStore(); const int numMessages = 100; var errors = new ConcurrentBag(); // Sequential append (MemStore is not thread-safe for writes) for (var i = 0; i < numMessages; i++) await store.AppendAsync($"test.{i % 10}", Encoding.UTF8.GetBytes($"payload-{i}"), default); // Concurrent reads await Parallel.ForEachAsync(Enumerable.Range(1, numMessages), async (seq, _) => { try { var msg = await store.LoadAsync((ulong)seq, default); msg.ShouldNotBeNull(); msg!.Sequence.ShouldBe((ulong)seq); } catch (Exception ex) { errors.Add(ex); } }); errors.ShouldBeEmpty(); } // --------------------------------------------------------------- // Go: TestNoRaceJetStreamApiRouter server/norace_test.go // --------------------------------------------------------------- [Fact] public async Task Concurrent_api_routing_is_thread_safe() { var meta = new JetStreamMetaGroup(3); var consumerManager = new ConsumerManager(meta); var streamManager = new StreamManager(meta, consumerManager: consumerManager); var router = new JetStreamApiRouter(streamManager, consumerManager, meta); streamManager.CreateOrUpdate(new StreamConfig { Name = "APIROUTE", Subjects = ["api.>"], Replicas = 1, }); var errors = new ConcurrentBag(); await Parallel.ForEachAsync(Enumerable.Range(0, 100), async (i, _) => { try { var subject = (i % 4) switch { 0 => JetStreamApiSubjects.Info, 1 => JetStreamApiSubjects.StreamNames, 2 => $"{JetStreamApiSubjects.StreamInfo}APIROUTE", _ => JetStreamApiSubjects.StreamList, }; var resp = router.Route(subject, "{}"u8); resp.ShouldNotBeNull(); } catch (Exception ex) { errors.Add(ex); } await Task.CompletedTask; }); errors.ShouldBeEmpty(); } // --------------------------------------------------------------- // Go: TestNoRaceRaftReplication server/norace_test.go // --------------------------------------------------------------- [Fact] public async Task Concurrent_replica_group_stepdown_and_propose() { var group = new StreamReplicaGroup("RACERAFT", replicas: 3); var errors = new ConcurrentBag(); var indices = new ConcurrentBag(); // Sequential operations (Raft serializes proposals through leader) for (var i = 0; i < 20; i++) { try { if (i % 5 == 0 && i > 0) await group.StepDownAsync(default); var idx = await group.ProposeAsync($"PUB event.{i}", default); indices.Add(idx); } catch (Exception ex) { errors.Add(ex); } } errors.ShouldBeEmpty(); indices.Count.ShouldBe(20); group.Leader.IsLeader.ShouldBeTrue(); } // --------------------------------------------------------------- // Go: TestNoRaceSublistReverseMatch server/norace_test.go // --------------------------------------------------------------- [Fact] public void SubList_reverse_match_concurrent_with_insert_is_safe() { using var subList = new SubList(); for (var i = 0; i < 50; i++) { subList.Insert(new Subscription { Subject = $"rev.{i % 10}.data", Sid = $"rev-{i}", }); } var errors = new ConcurrentBag(); Parallel.Invoke( () => { try { for (var i = 0; i < 100; i++) _ = subList.ReverseMatch($"rev.{i % 10}.data"); } catch (Exception ex) { errors.Add(ex); } }, () => { try { for (var i = 50; i < 100; i++) { subList.Insert(new Subscription { Subject = $"rev.{i % 10}.extra", Sid = $"revx-{i}", }); } } catch (Exception ex) { errors.Add(ex); } }); errors.ShouldBeEmpty(); } // --------------------------------------------------------------- // Go: TestNoRaceJetStreamConsumerListNames server/norace_test.go // --------------------------------------------------------------- [Fact] public void Concurrent_consumer_list_names_during_create_is_safe() { var consumerManager = new ConsumerManager(); var errors = new ConcurrentBag(); Parallel.Invoke( () => { try { for (var i = 0; i < 100; i++) { consumerManager.CreateOrUpdate("STREAM", new ConsumerConfig { DurableName = $"cln-{i}", }); } } catch (Exception ex) { errors.Add(ex); } }, () => { try { for (var i = 0; i < 200; i++) _ = consumerManager.ListNames("STREAM"); } catch (Exception ex) { errors.Add(ex); } }); errors.ShouldBeEmpty(); } // --------------------------------------------------------------- // Go: TestNoRaceStreamFindBySubject server/norace_test.go // --------------------------------------------------------------- [Fact] public void Concurrent_find_by_subject_during_create_is_safe() { var streamManager = new StreamManager(); var errors = new ConcurrentBag(); Parallel.Invoke( () => { try { for (var i = 0; i < 50; i++) { streamManager.CreateOrUpdate(new StreamConfig { Name = $"FBS-{i}", Subjects = [$"fbs{i}.>"], Replicas = 1, }); } } catch (Exception ex) { errors.Add(ex); } }, () => { try { for (var i = 0; i < 100; i++) _ = streamManager.FindBySubject($"fbs{i % 50}.test"); } catch (Exception ex) { errors.Add(ex); } }); errors.ShouldBeEmpty(); } }