From a660e385750a9a27a347b40a95cf971dfe967c1d Mon Sep 17 00:00:00 2001 From: Joseph Doherty Date: Fri, 27 Feb 2026 09:58:37 -0500 Subject: [PATCH] Implement deferred WaitQueue, DiskAvailability, and NoOpCache behavior with tests --- .../Auth/Ocsp/OcspTypes.cs | 96 +++++++++- .../JetStream/JetStreamApiTypes.cs | 23 ++- .../JetStream/StoreTypes.cs | 25 ++- .../JetStream/StreamTypes.cs | 164 +++++++++++++++++- .../Auth/OcspResponseCacheTests.cs | 21 ++- .../JetStream/DiskAvailabilityTests.cs | 58 +++++++ .../JetStream/NatsConsumerTests.cs | 78 +++++++++ .../JetStream/WaitQueueTests.cs | 20 +++ porting.db | Bin 3403776 -> 3403776 bytes reports/current.md | 12 +- reports/report_8849265.md | 36 ++++ 11 files changed, 508 insertions(+), 25 deletions(-) create mode 100644 dotnet/tests/ZB.MOM.NatsNet.Server.Tests/JetStream/DiskAvailabilityTests.cs create mode 100644 reports/report_8849265.md diff --git a/dotnet/src/ZB.MOM.NatsNet.Server/Auth/Ocsp/OcspTypes.cs b/dotnet/src/ZB.MOM.NatsNet.Server/Auth/Ocsp/OcspTypes.cs index b2c77d4..fe6742a 100644 --- a/dotnet/src/ZB.MOM.NatsNet.Server/Auth/Ocsp/OcspTypes.cs +++ b/dotnet/src/ZB.MOM.NatsNet.Server/Auth/Ocsp/OcspTypes.cs @@ -153,15 +153,105 @@ public interface IOcspResponseCache void Remove(string key); } +/// +/// Runtime counters for OCSP response cache behavior. +/// Mirrors Go OCSPResponseCacheStats shape. +/// +public sealed class OcspResponseCacheStats +{ + public long Responses { get; set; } + public long Hits { get; set; } + public long Misses { get; set; } + public long Revokes { get; set; } + public long Goods { get; set; } + public long Unknowns { get; set; } +} + /// /// A no-op OCSP cache that never stores anything. /// Mirrors Go NoOpCache in server/ocsp_responsecache.go. /// internal sealed class NoOpCache : IOcspResponseCache { - public byte[]? Get(string key) => null; - public void Put(string key, byte[] response) { } - public void Remove(string key) { } + private readonly Lock _mu = new(); + private readonly OcspResponseCacheConfig _config; + private OcspResponseCacheStats? _stats; + private bool _online; + + public NoOpCache() + : this(new OcspResponseCacheConfig { Type = "none" }) + { + } + + public NoOpCache(OcspResponseCacheConfig config) + { + _config = config; + } + + public byte[]? Get(string key) => null; + + public void Put(string key, byte[] response) { } + + public void Remove(string key) => Delete(key); + + public void Delete(string key) + { + _ = key; + } + + public void Start(NatsServer? server = null) + { + lock (_mu) + { + _stats = new OcspResponseCacheStats(); + _online = true; + } + } + + public void Stop(NatsServer? server = null) + { + lock (_mu) + { + _online = false; + } + } + + public bool Online() + { + lock (_mu) + { + return _online; + } + } + + public string Type() => "none"; + + public OcspResponseCacheConfig Config() + { + lock (_mu) + { + return _config; + } + } + + public OcspResponseCacheStats? Stats() + { + lock (_mu) + { + if (_stats is null) + return null; + + return new OcspResponseCacheStats + { + Responses = _stats.Responses, + Hits = _stats.Hits, + Misses = _stats.Misses, + Revokes = _stats.Revokes, + Goods = _stats.Goods, + Unknowns = _stats.Unknowns, + }; + } + } } /// diff --git a/dotnet/src/ZB.MOM.NatsNet.Server/JetStream/JetStreamApiTypes.cs b/dotnet/src/ZB.MOM.NatsNet.Server/JetStream/JetStreamApiTypes.cs index 53ea072..7234cbc 100644 --- a/dotnet/src/ZB.MOM.NatsNet.Server/JetStream/JetStreamApiTypes.cs +++ b/dotnet/src/ZB.MOM.NatsNet.Server/JetStream/JetStreamApiTypes.cs @@ -24,8 +24,27 @@ namespace ZB.MOM.NatsNet.Server; /// Stub: stored message type — full definition in session 20. public sealed class StoredMsg { } -/// Priority group for pull consumers — full definition in session 20. -public sealed class PriorityGroup { } +/// +/// Priority group for pull consumers. +/// Mirrors PriorityGroup in server/consumer.go. +/// +public sealed class PriorityGroup +{ + [JsonPropertyName("group")] + public string Group { get; set; } = string.Empty; + + [JsonPropertyName("min_pending")] + public long MinPending { get; set; } + + [JsonPropertyName("min_ack_pending")] + public long MinAckPending { get; set; } + + [JsonPropertyName("id")] + public string Id { get; set; } = string.Empty; + + [JsonPropertyName("priority")] + public int Priority { get; set; } +} // --------------------------------------------------------------------------- // API subject constants diff --git a/dotnet/src/ZB.MOM.NatsNet.Server/JetStream/StoreTypes.cs b/dotnet/src/ZB.MOM.NatsNet.Server/JetStream/StoreTypes.cs index 46434ec..442847e 100644 --- a/dotnet/src/ZB.MOM.NatsNet.Server/JetStream/StoreTypes.cs +++ b/dotnet/src/ZB.MOM.NatsNet.Server/JetStream/StoreTypes.cs @@ -970,20 +970,21 @@ public static class DiskAvailability private const long JetStreamMaxStoreDefault = 1L * 1024 * 1024 * 1024 * 1024; /// - /// Returns approximately 75% of available disk space at . - /// Returns (1 TB) if the check fails. + /// Returns approximately 75% of available disk space at . + /// Ensures the directory exists before probing and falls back to the default + /// cap if disk probing fails. /// - public static long Available(string path) + public static long DiskAvailable(string storeDir) { - // TODO: session 17 — implement via DriveInfo or P/Invoke statvfs on non-Windows. try { - var drive = new DriveInfo(Path.GetPathRoot(Path.GetFullPath(path)) ?? path); + if (!string.IsNullOrWhiteSpace(storeDir)) + Directory.CreateDirectory(storeDir); + + var root = Path.GetPathRoot(Path.GetFullPath(storeDir)); + var drive = new DriveInfo(root ?? storeDir); if (drive.IsReady) - { - // Estimate 75% of available free space, matching Go behaviour. return drive.AvailableFreeSpace / 4 * 3; - } } catch { @@ -993,8 +994,14 @@ public static class DiskAvailability return JetStreamMaxStoreDefault; } + /// + /// Returns approximately 75% of available disk space at . + /// Returns (1 TB) if the check fails. + /// + public static long Available(string path) => DiskAvailable(path); + /// /// Returns true if at least bytes are available at . /// - public static bool Check(string path, long needed) => Available(path) >= needed; + public static bool Check(string path, long needed) => DiskAvailable(path) >= needed; } diff --git a/dotnet/src/ZB.MOM.NatsNet.Server/JetStream/StreamTypes.cs b/dotnet/src/ZB.MOM.NatsNet.Server/JetStream/StreamTypes.cs index c68dafc..5244c6c 100644 --- a/dotnet/src/ZB.MOM.NatsNet.Server/JetStream/StreamTypes.cs +++ b/dotnet/src/ZB.MOM.NatsNet.Server/JetStream/StreamTypes.cs @@ -409,6 +409,9 @@ public sealed class WaitingRequest /// Bytes accumulated so far. public int B { get; set; } + + /// Optional pull request priority group metadata. + public PriorityGroup? PriorityGroup { get; set; } } /// @@ -418,9 +421,15 @@ public sealed class WaitingRequest public sealed class WaitQueue { private readonly List _reqs = new(); + private readonly int _max; private int _head; private int _tail; + public WaitQueue(int max = 0) + { + _max = max; + } + /// Number of pending requests in the queue. public int Len => _tail - _head; @@ -432,6 +441,43 @@ public sealed class WaitQueue _tail++; } + /// + /// Add a waiting request ordered by priority while preserving FIFO order + /// within each priority level. + /// + public bool AddPrioritized(WaitingRequest req) + { + ArgumentNullException.ThrowIfNull(req); + if (IsFull(_max)) + return false; + InsertSorted(req); + return true; + } + + /// Insert a request in priority order (lower number = higher priority). + public void InsertSorted(WaitingRequest req) + { + ArgumentNullException.ThrowIfNull(req); + + if (Len == 0) + { + Add(req); + return; + } + + var priority = PriorityOf(req); + var insertAt = _head; + while (insertAt < _tail) + { + if (PriorityOf(_reqs[insertAt]) > priority) + break; + insertAt++; + } + + _reqs.Insert(insertAt, req); + _tail++; + } + /// Peek at the head request without removing it. public WaitingRequest? Peek() { @@ -443,13 +489,123 @@ public sealed class WaitQueue /// Remove and return the head request. public WaitingRequest? Pop() { - if (Len == 0) + var wr = Peek(); + if (wr is null) return null; - var req = _reqs[_head++]; + wr.D++; + wr.N--; + if (wr.N > 0 && Len > 1) + { + RemoveCurrent(); + Add(wr); + } + else if (wr.N <= 0) + { + RemoveCurrent(); + } + + return wr; + } + + /// Returns true if the queue contains no active requests. + public bool IsEmpty() => Len == 0; + + /// Rotate the head request to the tail. + public void Cycle() + { + var wr = Peek(); + if (wr is null) + return; + + RemoveCurrent(); + Add(wr); + } + + /// Pop strategy used by pull consumers based on priority policy. + public WaitingRequest? PopOrPopAndRequeue(PriorityPolicy priority) + => priority == PriorityPolicy.PriorityPrioritized ? PopAndRequeue() : Pop(); + + /// + /// Pop and requeue to the end of the same priority band while preserving + /// stable order within that band. + /// + public WaitingRequest? PopAndRequeue() + { + var wr = Peek(); + if (wr is null) + return null; + + wr.D++; + wr.N--; + + if (wr.N > 0 && Len > 1) + { + // Remove the current head and insert it back in priority order. + _reqs.RemoveAt(_head); + _tail--; + InsertSorted(wr); + } + else if (wr.N <= 0) + { + RemoveCurrent(); + } + + return wr; + } + + /// Remove the current head request from the queue. + public void RemoveCurrent() => Remove(null, Peek()); + + /// Remove a specific request from the queue. + public void Remove(WaitingRequest? pre, WaitingRequest? wr) + { + if (wr is null || Len == 0) + return; + + var removeAt = -1; + + if (pre is not null) + { + for (var i = _head; i < _tail; i++) + { + if (!ReferenceEquals(_reqs[i], pre)) + continue; + + var candidate = i + 1; + if (candidate < _tail && ReferenceEquals(_reqs[candidate], wr)) + removeAt = candidate; + break; + } + } + + if (removeAt < 0) + { + for (var i = _head; i < _tail; i++) + { + if (ReferenceEquals(_reqs[i], wr)) + { + removeAt = i; + break; + } + } + } + + if (removeAt < 0) + return; + + if (removeAt == _head) + { + _head++; + } + else + { + _reqs.RemoveAt(removeAt); + _tail--; + } + if (_head > 32 && _head * 2 >= _tail) Compress(); - return req; } /// Compact the internal backing list to reclaim removed slots. @@ -470,6 +626,8 @@ public sealed class WaitQueue return false; return Len >= max; } + + private static int PriorityOf(WaitingRequest req) => req.PriorityGroup?.Priority ?? int.MaxValue; } /// diff --git a/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/Auth/OcspResponseCacheTests.cs b/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/Auth/OcspResponseCacheTests.cs index 718270e..d568544 100644 --- a/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/Auth/OcspResponseCacheTests.cs +++ b/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/Auth/OcspResponseCacheTests.cs @@ -31,13 +31,30 @@ public sealed class OcspResponseCacheTests } [Fact] - public void NoOpCache_AndMonitor_ShouldNoOpSafely() + public void NoOpCache_LifecycleAndStats_ShouldNoOpSafely() { var noOp = new NoOpCache(); + noOp.Online().ShouldBeFalse(); + noOp.Type().ShouldBe("none"); + noOp.Config().ShouldNotBeNull(); + noOp.Stats().ShouldBeNull(); + + noOp.Start(); + noOp.Online().ShouldBeTrue(); + noOp.Stats().ShouldNotBeNull(); + noOp.Put("k", [5]); noOp.Get("k").ShouldBeNull(); - noOp.Remove("k"); + noOp.Remove("k"); // alias to Delete + noOp.Delete("k"); + noOp.Stop(); + noOp.Online().ShouldBeFalse(); + } + + [Fact] + public void OcspMonitor_StartAndStop_ShouldLoadStaple() + { var dir = Path.Combine(Path.GetTempPath(), $"ocsp-monitor-{Guid.NewGuid():N}"); Directory.CreateDirectory(dir); try diff --git a/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/JetStream/DiskAvailabilityTests.cs b/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/JetStream/DiskAvailabilityTests.cs new file mode 100644 index 0000000..c7c8936 --- /dev/null +++ b/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/JetStream/DiskAvailabilityTests.cs @@ -0,0 +1,58 @@ +// Copyright 2012-2026 The NATS Authors +// Licensed under the Apache License, Version 2.0 + +using Shouldly; +using ZB.MOM.NatsNet.Server; + +namespace ZB.MOM.NatsNet.Server.Tests.JetStream; + +public sealed class DiskAvailabilityTests +{ + private const long JetStreamMaxStoreDefault = 1L * 1024 * 1024 * 1024 * 1024; + + [Fact] + public void DiskAvailable_MissingDirectory_ShouldCreateDirectory() + { + var root = Path.Combine(Path.GetTempPath(), $"disk-avail-{Guid.NewGuid():N}"); + var target = Path.Combine(root, "nested"); + try + { + Directory.Exists(target).ShouldBeFalse(); + + var available = DiskAvailability.DiskAvailable(target); + + Directory.Exists(target).ShouldBeTrue(); + available.ShouldBeGreaterThan(0L); + } + finally + { + if (Directory.Exists(root)) + Directory.Delete(root, recursive: true); + } + } + + [Fact] + public void DiskAvailable_InvalidPath_ShouldReturnFallback() + { + var available = DiskAvailability.DiskAvailable("\0"); + available.ShouldBe(JetStreamMaxStoreDefault); + } + + [Fact] + public void Check_ShouldUseDiskAvailableThreshold() + { + var root = Path.Combine(Path.GetTempPath(), $"disk-check-{Guid.NewGuid():N}"); + try + { + var available = DiskAvailability.DiskAvailable(root); + + DiskAvailability.Check(root, Math.Max(0, available - 1)).ShouldBeTrue(); + DiskAvailability.Check(root, available + 1).ShouldBeFalse(); + } + finally + { + if (Directory.Exists(root)) + Directory.Delete(root, recursive: true); + } + } +} diff --git a/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/JetStream/NatsConsumerTests.cs b/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/JetStream/NatsConsumerTests.cs index e73f9f1..2ae7f32 100644 --- a/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/JetStream/NatsConsumerTests.cs +++ b/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/JetStream/NatsConsumerTests.cs @@ -35,4 +35,82 @@ public sealed class NatsConsumerTests consumer.Stop(); consumer.IsLeader().ShouldBeFalse(); } + + [Fact] // T:1364 + public void SortingConsumerPullRequests_ShouldSucceed() + { + var q = new WaitQueue(max: 100); + + q.AddPrioritized(new WaitingRequest { Reply = "1a", PriorityGroup = new PriorityGroup { Priority = 1 }, N = 1 }) + .ShouldBeTrue(); + q.AddPrioritized(new WaitingRequest { Reply = "2a", PriorityGroup = new PriorityGroup { Priority = 2 }, N = 1 }) + .ShouldBeTrue(); + q.AddPrioritized(new WaitingRequest { Reply = "1b", PriorityGroup = new PriorityGroup { Priority = 1 }, N = 1 }) + .ShouldBeTrue(); + q.AddPrioritized(new WaitingRequest { Reply = "2b", PriorityGroup = new PriorityGroup { Priority = 2 }, N = 1 }) + .ShouldBeTrue(); + q.AddPrioritized(new WaitingRequest { Reply = "1c", PriorityGroup = new PriorityGroup { Priority = 1 }, N = 1 }) + .ShouldBeTrue(); + q.AddPrioritized(new WaitingRequest { Reply = "3a", PriorityGroup = new PriorityGroup { Priority = 3 }, N = 1 }) + .ShouldBeTrue(); + q.AddPrioritized(new WaitingRequest { Reply = "2c", PriorityGroup = new PriorityGroup { Priority = 2 }, N = 1 }) + .ShouldBeTrue(); + + var expectedOrder = new[] + { + ("1a", 1), + ("1b", 1), + ("1c", 1), + ("2a", 2), + ("2b", 2), + ("2c", 2), + ("3a", 3), + }; + + q.Len.ShouldBe(expectedOrder.Length); + foreach (var (reply, priority) in expectedOrder) + { + var current = q.Peek(); + current.ShouldNotBeNull(); + current!.Reply.ShouldBe(reply); + current.PriorityGroup.ShouldNotBeNull(); + current.PriorityGroup!.Priority.ShouldBe(priority); + q.RemoveCurrent(); + } + + q.IsEmpty().ShouldBeTrue(); + } + + [Fact] // T:1365 + public void WaitQueuePopAndRequeue_ShouldSucceed() + { + var q = new WaitQueue(max: 100); + q.AddPrioritized(new WaitingRequest { Reply = "1a", N = 2, PriorityGroup = new PriorityGroup { Priority = 1 } }) + .ShouldBeTrue(); + q.AddPrioritized(new WaitingRequest { Reply = "1b", N = 1, PriorityGroup = new PriorityGroup { Priority = 1 } }) + .ShouldBeTrue(); + q.AddPrioritized(new WaitingRequest { Reply = "2a", N = 3, PriorityGroup = new PriorityGroup { Priority = 2 } }) + .ShouldBeTrue(); + + var wr = q.PopAndRequeue(); + wr.ShouldNotBeNull(); + wr!.Reply.ShouldBe("1a"); + wr.N.ShouldBe(1); + q.Len.ShouldBe(3); + + wr = q.PopAndRequeue(); + wr.ShouldNotBeNull(); + wr!.Reply.ShouldBe("1b"); + wr.N.ShouldBe(0); + q.Len.ShouldBe(2); + + wr = q.PopAndRequeue(); + wr.ShouldNotBeNull(); + wr!.Reply.ShouldBe("1a"); + wr.N.ShouldBe(0); + q.Len.ShouldBe(1); + + q.Peek()!.Reply.ShouldBe("2a"); + q.Peek()!.N.ShouldBe(3); + } } diff --git a/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/JetStream/WaitQueueTests.cs b/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/JetStream/WaitQueueTests.cs index 6c12bb9..215360b 100644 --- a/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/JetStream/WaitQueueTests.cs +++ b/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/JetStream/WaitQueueTests.cs @@ -24,8 +24,28 @@ public sealed class WaitQueueTests q.Peek()!.Subject.ShouldBe("A"); q.Pop()!.Subject.ShouldBe("A"); + q.Pop()!.Subject.ShouldBe("B"); + q.Len.ShouldBe(1); + q.Pop()!.Subject.ShouldBe("B"); q.Len.ShouldBe(0); q.IsFull(1).ShouldBeFalse(); } + + [Fact] + public void AddPrioritized_AndCycle_ShouldPreserveStableOrder() + { + var q = new WaitQueue(max: 10); + + q.AddPrioritized(new WaitingRequest { Reply = "2a", N = 1, PriorityGroup = new PriorityGroup { Priority = 2 } }) + .ShouldBeTrue(); + q.AddPrioritized(new WaitingRequest { Reply = "1a", N = 1, PriorityGroup = new PriorityGroup { Priority = 1 } }) + .ShouldBeTrue(); + q.AddPrioritized(new WaitingRequest { Reply = "1b", N = 1, PriorityGroup = new PriorityGroup { Priority = 1 } }) + .ShouldBeTrue(); + + q.Peek()!.Reply.ShouldBe("1a"); + q.Cycle(); + q.Peek()!.Reply.ShouldBe("1b"); + } } diff --git a/porting.db b/porting.db index 9d9fd66ee338aeadc5d3a2897061bceb1a038e14..3e0840d6f996ebba219f567da5561c6edb3606dc 100644 GIT binary patch delta 2255 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 diff --git a/reports/current.md b/reports/current.md index 5239520..3fab8dd 100644 --- a/reports/current.md +++ b/reports/current.md @@ -1,6 +1,6 @@ # NATS .NET Porting Status Report -Generated: 2026-02-27 13:56:27 UTC +Generated: 2026-02-27 14:58:38 UTC ## Modules (12 total) @@ -12,17 +12,17 @@ Generated: 2026-02-27 13:56:27 UTC | Status | Count | |--------|-------| -| deferred | 2461 | +| deferred | 2440 | | n_a | 18 | -| verified | 1194 | +| verified | 1215 | ## Unit Tests (3257 total) | Status | Count | |--------|-------| -| deferred | 2662 | +| deferred | 2660 | | n_a | 187 | -| verified | 408 | +| verified | 410 | ## Library Mappings (36 total) @@ -33,4 +33,4 @@ Generated: 2026-02-27 13:56:27 UTC ## Overall Progress -**1819/6942 items complete (26.2%)** +**1842/6942 items complete (26.5%)** diff --git a/reports/report_8849265.md b/reports/report_8849265.md new file mode 100644 index 0000000..3fab8dd --- /dev/null +++ b/reports/report_8849265.md @@ -0,0 +1,36 @@ +# NATS .NET Porting Status Report + +Generated: 2026-02-27 14:58:38 UTC + +## Modules (12 total) + +| Status | Count | +|--------|-------| +| verified | 12 | + +## Features (3673 total) + +| Status | Count | +|--------|-------| +| deferred | 2440 | +| n_a | 18 | +| verified | 1215 | + +## Unit Tests (3257 total) + +| Status | Count | +|--------|-------| +| deferred | 2660 | +| n_a | 187 | +| verified | 410 | + +## Library Mappings (36 total) + +| Status | Count | +|--------|-------| +| mapped | 36 | + + +## Overall Progress + +**1842/6942 items complete (26.5%)**