docs: add design for sections 3-6 gaps implementation

This commit is contained in:
Joseph Doherty
2026-02-23 00:17:24 -05:00
parent 3941c85e76
commit f86ea57f43

View File

@@ -0,0 +1,190 @@
# Sections 3-6 Gaps Implementation Design
> Approved 2026-02-23. Implements all remaining gaps in Protocol Parsing, Subscriptions & Subject Matching, Authentication & Authorization, and Configuration.
---
## Section 3 — Protocol Parsing
### 3a. INFO Serialization Caching
Add `byte[] _infoJsonCache` on `NatsServer`. Build once in `StartAsync()` after `ServerInfo` is populated. Rebuild only on config reload.
`NatsClient.SendInfo()` uses cached bytes for non-nonce connections. For NKey connections (which need a per-connection nonce), clone `ServerInfo`, set nonce, serialize fresh.
**Files:** `NatsServer.cs`, `NatsClient.cs`
### 3b. Protocol Tracing
Add `ILogger` to `NatsParser`. Add `TraceInOp(ReadOnlySpan<byte> op, ReadOnlySpan<byte> arg)` that logs at `LogLevel.Trace` after each command dispatch in `TryParse()`.
Controlled by `NatsOptions.Trace` (which sets log level filter).
**Files:** `NatsParser.cs`, `NatsOptions.cs`
### 3c. MIME Header Parsing
New `NatsHeaderParser` static class in `Protocol/`. Parses `NATS/1.0 <status> <description>\r\n` status line + `Key: Value\r\n` pairs.
Returns `NatsHeaders` readonly struct with `Status` (int), `Description` (string), and key-value `Dictionary<string, string[]>` lookup.
Used in `ProcessMessage()` for header inspection (no-responders status, future message tracing).
**Files:** New `Protocol/NatsHeaderParser.cs`, `NatsServer.cs`
### 3d. MSG/HMSG Construction Optimization
Pre-allocate buffers for MSG/HMSG prefix using `Span<byte>` and `Utf8Formatter` instead of string interpolation + `Encoding.UTF8.GetBytes()`. Reduces per-message allocations.
**Files:** `NatsClient.cs`
---
## Section 4 — Subscriptions & Subject Matching
### 4a. Atomic Generation ID for Cache Invalidation
Add `long _generation` to `SubList`. Increment via `Interlocked.Increment` on every `Insert()` and `Remove()`. Cache entries store generation at computation time. On `Match()`, stale generation = cache miss.
Replaces current explicit per-key cache removal. O(1) invalidation.
**Files:** `SubList.cs`
### 4b. Async Background Cache Sweep
Replace inline sweep (runs under write lock) with `PeriodicTimer`-based background task. Acquires write lock briefly to snapshot + evict stale entries. Triggered when cache count > 1024, sweeps to 256.
**Files:** `SubList.cs`
### 4c. `plist` Optimization for High-Fanout Nodes
On `TrieNode`, when `PlainSubs.Count > 256`, convert `HashSet<Subscription>` to `Subscription[]` flat array in `PList` field. `Match()` iterates `PList` when present. On count < 128, convert back to HashSet.
**Files:** `SubList.cs`
### 4d. SubList Utility Methods
| Method | Description |
|--------|-------------|
| `Stats()` | Returns `SubListStats` record with NumSubs, NumCache, NumInserts, NumRemoves, NumMatches, CacheHitRate, MaxFanout. Track via `Interlocked` counters. |
| `HasInterest(string)` | Walk trie without building result, return true on first hit. |
| `NumInterest(string)` | Walk trie, count without allocating result arrays. |
| `ReverseMatch(string)` | Walk trie with literal tokens, collect all subscription patterns matching the literal. |
| `RemoveBatch(IEnumerable<Subscription>)` | Single write-lock, batch removes, single generation bump. |
| `All()` | Depth-first trie walk, yield all subscriptions. Returns `IReadOnlyList<Subscription>`. |
| `MatchBytes(ReadOnlySpan<byte>)` | Zero-copy match using byte-based `TokenEnumerator`. |
**Files:** `SubList.cs`, new `SubListStats.cs`
### 4e. SubjectMatch Utilities
| Method | Description |
|--------|-------------|
| `SubjectsCollide(string, string)` | Token-by-token comparison handling `*` and `>`. Two patterns collide if any literal could match both. |
| `TokenAt(string, int)` | Return nth dot-delimited token as `ReadOnlySpan<char>`. |
| `NumTokens(string)` | Count dots + 1. |
| UTF-8/null validation | Add `checkRunes` parameter to `IsValidSubject()`. When true, scan for `\0` bytes and validate UTF-8. |
**Files:** `SubjectMatch.cs`
### 4f. Per-Account Subscription Limits
Add `MaxSubscriptions` to `Account`. Track `SubscriptionCount` via `Interlocked`. Enforce in `ProcessSub()`. Close with `ClientClosedReason.MaxSubscriptionsExceeded`.
**Files:** `Account.cs`, `NatsClient.cs`
---
## Section 5 — Authentication & Authorization
### 5a. Deny List Enforcement at Delivery Time
In `NatsServer.DeliverMessage()`, before sending MSG, check `subscriber.Client.Permissions?.IsDeliveryAllowed(subject)`. New method checks publish deny list for the receiving client ("msg delivery filter"). Cache results in pub cache.
**Files:** `NatsServer.cs`, `ClientPermissions.cs`
### 5b. Permission Caching with 128-Entry LRU
Replace `ConcurrentDictionary<string, bool>` with custom `PermissionLruCache` (128 entries). `Dictionary<string, LinkedListNode>` + `LinkedList` for LRU ordering. Lock-protected (per-client, low contention).
**Files:** `ClientPermissions.cs`, new `Auth/PermissionLruCache.cs`
### 5c. Subscribe Deny Queue-Group Checking
`IsSubscribeAllowed(subject, queue)` checks queue group against subscribe deny list (currently ignores queue parameter).
**Files:** `ClientPermissions.cs`
### 5d. Response Permissions (Reply Tracking)
New `ResponseTracker` class on `NatsClient`. Created when `Permissions.Response` is non-null. Tracks reply subjects with TTL (`ResponsePermission.Expires`) and max count (`ResponsePermission.MaxMsgs`). `IsPublishAllowed()` consults tracker for reply subjects not in static allow list. Expired entries cleaned lazily + in ping timer.
**Files:** New `Auth/ResponseTracker.cs`, `ClientPermissions.cs`, `NatsClient.cs`
### 5e. Per-Account Connection Limits
Add `MaxConnections` to `Account`. Enforce in `ProcessConnectAsync()` after account assignment. Reject with `-ERR maximum connections for account exceeded`.
**Files:** `Account.cs`, `NatsClient.cs`
### 5f. Multi-Account User Resolution
Add `NatsOptions.Accounts` as `Dictionary<string, AccountConfig>` with per-account MaxConnections, MaxSubscriptions, DefaultPermissions. `AuthService` resolves account name to configured `Account` with limits.
**Files:** `NatsOptions.cs`, new `Auth/AccountConfig.cs`, `AuthService.cs`, `NatsServer.cs`
### 5g. Auth Expiry Enforcement
In `ProcessConnectAsync()`, if `AuthResult.Expiry` is set, start `CancellationTokenSource.CancelAfter(expiry - now)`. Link to client lifetime. On fire, close with `ClientClosedReason.AuthenticationExpired`.
**Files:** `NatsClient.cs`
### 5h. Auto-Unsub Cleanup
In `DeliverMessage()`, when `sub.MessageCount >= sub.MaxMessages`, call `sub.Client.RemoveSubscription(sub.Sid)` and `subList.Remove(sub)` to clean up both tracking dict and trie. Currently only skips delivery.
**Files:** `NatsServer.cs`, `NatsClient.cs`
---
## Section 6 — Configuration
### 6a. Debug/Trace CLI Flags
Add `-D`/`--debug`, `-V`/`--trace`, `-DV` to `Program.cs`. Set `NatsOptions.Debug` and `NatsOptions.Trace`. Wire into Serilog minimum level.
**Files:** `Program.cs`
### 6b. New NatsOptions Fields
| Field | Type | Default | Purpose |
|-------|------|---------|---------|
| `MaxSubs` | int | 0 (unlimited) | Per-connection subscription limit |
| `MaxSubTokens` | int | 0 (unlimited) | Max tokens in a subject |
| `Debug` | bool | false | Enable debug-level logging |
| `Trace` | bool | false | Enable trace-level protocol logging |
| `LogFile` | string? | null | Log to file instead of console |
| `LogSizeLimit` | long | 0 | Max log file size before rotation |
| `Tags` | Dictionary<string, string>? | null | Server metadata tags |
**Files:** `NatsOptions.cs`
### 6c. Logging Options Wiring
In `Program.cs`, if `LogFile` is set, add Serilog `File` sink with `LogSizeLimit`. If `Debug`/`Trace`, override Serilog minimum level.
**Files:** `Program.cs`
---
## Implementation Strategy
Execute in a git worktree. Parallelize where files don't overlap:
- **Parallel batch 1:** SubjectMatch utilities (4e) | NatsOptions + CLI flags (6a, 6b) | NatsHeaderParser (3c) | PermissionLruCache (5b) | SubListStats (4d stats class)
- **Parallel batch 2:** SubList overhaul (4a, 4b, 4c, 4d methods) | Account limits + config (4f, 5e, 5f, 5g) | Response tracker (5d)
- **Parallel batch 3:** Protocol tracing (3b) | INFO caching (3a) | MSG optimization (3d)
- **Sequential:** Delivery-time enforcement (5a, 5c, 5h) — touches NatsServer.cs + ClientPermissions.cs, must be coordinated
- **Final:** Logging wiring (6c) | differences.md update
Tests added alongside each feature in the appropriate test file.