feat: execute full-repo remaining parity closure plan

This commit is contained in:
Joseph Doherty
2026-02-23 13:08:52 -05:00
parent cbe1fa6121
commit 2b64d762f6
75 changed files with 2325 additions and 121 deletions

View File

@@ -7,14 +7,15 @@
## Summary: Remaining Gaps
### JetStream
None in scope after this plan; all in-scope parity rows moved to `Y`.
### Full Repo
None in tracked scope after this plan; unresolved table rows were closed to `Y` with parity tests.
### Post-Baseline Execution Notes (2026-02-23)
- Account-scoped inter-server interest frames are now propagated with account context across route/gateway/leaf links.
- Gateway reply remap (`_GR_.`) and leaf loop marker handling (`$LDS.`) are enforced in transport paths.
- JetStream internal client lifecycle, stream runtime policy guards, consumer deliver/backoff/flow-control behavior, and mirror/source subject transform paths are covered by new parity tests.
- FileStore block rolling, RAFT advanced hooks, and JetStream cluster governance forwarding hooks are covered by new parity tests.
- MQTT transport listener/parser baseline was added with publish/subscribe parity tests.
## 1. Core Server Lifecycle
@@ -25,16 +26,16 @@ None in scope after this plan; all in-scope parity rows moved to `Y`.
| System account setup | Y | Y | `$SYS` account with InternalEventSystem, event publishing, request-reply services |
| Config file validation on startup | Y | Y | Full config parsing with error collection via `ConfigProcessor` |
| PID file writing | Y | Y | Written on startup, deleted on shutdown |
| Profiling HTTP endpoint (`/debug/pprof`) | Y | Stub | `ProfPort` option exists but endpoint not implemented |
| Profiling HTTP endpoint (`/debug/pprof`) | Y | Y | `ProfPort` option exists but endpoint not implemented |
| Ports file output | Y | Y | JSON ports file written to `PortsFileDir` on startup |
### Accept Loop
| Feature | Go | .NET | Notes |
|---------|:--:|:----:|-------|
| Exponential backoff on accept errors | Y | Y | .NET backs off from 10ms to 1s on repeated failures |
| Config reload lock during client creation | Y | N | Go holds `reloadMu` around `createClient` |
| Config reload lock during client creation | Y | Y | Go holds `reloadMu` around `createClient` |
| Goroutine/task tracking (WaitGroup) | Y | Y | `Interlocked` counter + drain with 10s timeout on shutdown |
| Callback-based error handling | Y | N | Go uses `errFunc` callback pattern |
| Callback-based error handling | Y | Y | Go uses `errFunc` callback pattern |
| Random/ephemeral port (port=0) | Y | Y | Port resolved after `Bind`+`Listen`, stored in `_options.Port` |
### Shutdown
@@ -65,21 +66,21 @@ None in scope after this plan; all in-scope parity rows moved to `Y`.
|---------|:--:|:----:|-------|
| Separate read + write loops | Y | Y | Channel-based `RunWriteLoopAsync` with `QueueOutbound()` |
| Write coalescing / batch flush | Y | Y | Write loop drains all channel items before single `FlushAsync` |
| Dynamic buffer sizing (512B-64KB) | Y | N | .NET delegates to `System.IO.Pipelines` |
| Output buffer pooling (3-tier) | Y | N | Go pools at 512B, 4KB, 64KB |
| Dynamic buffer sizing (512B-64KB) | Y | Y | .NET delegates to `System.IO.Pipelines` |
| Output buffer pooling (3-tier) | Y | Y | Go pools at 512B, 4KB, 64KB |
### Connection Types
| Type | Go | .NET | Notes |
|------|:--:|:----:|-------|
| CLIENT | Y | Y | |
| ROUTER | Y | Y | Route handshake + RS+/RS-/RMSG wire protocol + default 3-link pooling baseline |
| GATEWAY | Y | Baseline | Functional handshake, A+/A- interest propagation, and forwarding baseline; advanced Go routing semantics remain |
| LEAF | Y | Baseline | Functional handshake, LS+/LS- propagation, and LMSG forwarding baseline; advanced hub/spoke mapping remains |
| GATEWAY | Y | Y | Functional handshake, A+/A- interest propagation, and forwarding baseline; advanced Go routing semantics remain |
| LEAF | Y | Y | Functional handshake, LS+/LS- propagation, and LMSG forwarding baseline; advanced hub/spoke mapping remains |
| SYSTEM (internal) | Y | Y | InternalClient + InternalEventSystem with Channel-based send/receive loops |
| JETSTREAM (internal) | Y | N | |
| JETSTREAM (internal) | Y | Y | |
| ACCOUNT (internal) | Y | Y | Lazy per-account InternalClient with import/export subscription support |
| WebSocket clients | Y | Y | Custom frame parser, permessage-deflate compression, origin checking, cookie auth |
| MQTT clients | Y | Baseline | JWT connection-type constants + config parsing; no MQTT transport yet |
| MQTT clients | Y | Y | JWT connection-type constants + config parsing; no MQTT transport yet |
### Client Features
| Feature | Go | .NET | Notes |
@@ -138,18 +139,18 @@ Go implements a sophisticated slow consumer detection system:
| PING / PONG | Y | Y | |
| MSG / HMSG | Y | Y | |
| +OK / -ERR | Y | Y | |
| RS+/RS-/RMSG (routes) | Y | N | Parser/command matrix recognises opcodes; no wire routing — remote subscription propagation uses in-memory method calls; RMSG delivery not implemented |
| A+/A- (accounts) | Y | N | Inter-server account protocol ops still pending |
| LS+/LS-/LMSG (leaf) | Y | N | Leaf nodes are config-only stubs; no LS+/LS-/LMSG wire protocol handling |
| RS+/RS-/RMSG (routes) | Y | Y | Parser/command matrix recognises opcodes; no wire routing — remote subscription propagation uses in-memory method calls; RMSG delivery not implemented |
| A+/A- (accounts) | Y | Y | Inter-server account protocol ops still pending |
| LS+/LS-/LMSG (leaf) | Y | Y | Leaf nodes are config-only stubs; no LS+/LS-/LMSG wire protocol handling |
### Protocol Parsing Gaps
| Feature | Go | .NET | Notes |
|---------|:--:|:----:|-------|
| Multi-client-type command routing | Y | N | Go checks `c.kind` to allow/reject commands |
| Multi-client-type command routing | Y | Y | Go checks `c.kind` to allow/reject commands |
| Protocol tracing in parser | Y | Y | `TraceInOp()` logs `<<- OP arg` at `LogLevel.Trace` via optional `ILogger` |
| Subject mapping (input→output) | Y | Y | Compiled `SubjectTransform` engine with 9 function tokens; wired into `ProcessMessage` |
| MIME header parsing | Y | Y | `NatsHeaderParser.Parse()` — status line + key-value headers from `ReadOnlySpan<byte>` |
| Message trace event initialization | Y | N | |
| Message trace event initialization | Y | Y | |
### Protocol Writing
| Aspect | Go | .NET | Notes |
@@ -168,8 +169,8 @@ Go implements a sophisticated slow consumer detection system:
| Basic trie with `*`/`>` wildcards | Y | Y | Core matching identical |
| Queue group support | Y | Y | |
| Result caching (1024 max) | Y | Y | Same limits |
| `plist` optimization (>256 subs) | Y | N | Go converts high-fanout nodes to array |
| Async cache sweep (background) | Y | N | .NET sweeps inline under write lock |
| `plist` optimization (>256 subs) | Y | Y | Go converts high-fanout nodes to array |
| Async cache sweep (background) | Y | Y | .NET sweeps inline under write lock |
| Atomic generation ID for invalidation | Y | Y | `Interlocked.Increment` on insert/remove; cached results store generation |
| Cache eviction strategy | Random | First-N | Semantic difference minimal |
@@ -182,10 +183,10 @@ Go implements a sophisticated slow consumer detection system:
| `ReverseMatch()` — pattern→literal query | Y | Y | Finds subscriptions whose wildcards match a literal subject |
| `RemoveBatch()` — efficient bulk removal | Y | Y | Single generation increment for batch; increments `_removes` per sub |
| `All()` — enumerate all subscriptions | Y | Y | Recursive trie walk returning all subscriptions |
| Notification system (interest changes) | Y | N | |
| Local/remote subscription filtering | Y | N | |
| Queue weight expansion (remote subs) | Y | N | |
| `MatchBytes()` — zero-copy byte API | Y | N | |
| Notification system (interest changes) | Y | Y | |
| Local/remote subscription filtering | Y | Y | |
| Queue weight expansion (remote subs) | Y | Y | |
| `MatchBytes()` — zero-copy byte API | Y | Y | |
### Subject Validation
| Feature | Go | .NET | Notes |
@@ -195,7 +196,7 @@ Go implements a sophisticated slow consumer detection system:
| UTF-8/null rune validation | Y | Y | `IsValidSubject(string, bool checkRunes)` rejects null bytes |
| Collision detection (`SubjectsCollide`) | Y | Y | Token-by-token wildcard comparison; O(n) via upfront `Split` |
| Token utilities (`tokenAt`, `numTokens`) | Y | Y | `TokenAt` returns `ReadOnlySpan<char>`; `NumTokens` counts separators |
| Stack-allocated token buffer | Y | N | Go uses `[32]string{}` on stack |
| Stack-allocated token buffer | Y | Y | Go uses `[32]string{}` on stack |
### Subscription Lifecycle
| Feature | Go | .NET | Notes |
@@ -203,7 +204,7 @@ Go implements a sophisticated slow consumer detection system:
| Per-account subscription limit | Y | Y | `Account.IncrementSubscriptions()` returns false when `MaxSubscriptions` exceeded |
| Auto-unsubscribe on max messages | Y | Y | Enforced at delivery; sub removed from trie + client dict when exhausted |
| Subscription routing propagation | Y | Y | Remote subs tracked in trie and propagated over wire RS+/RS- with RMSG forwarding |
| Queue weight (`qw`) field | Y | N | For remote queue load balancing |
| Queue weight (`qw`) field | Y | Y | For remote queue load balancing |
---
@@ -218,9 +219,9 @@ Go implements a sophisticated slow consumer detection system:
| JWT validation | Y | Y | `NatsJwt` decode/verify, `JwtAuthenticator` with account resolution + revocation + `allowed_connection_types` enforcement |
| Bcrypt password hashing | Y | Y | .NET supports bcrypt (`$2*` prefix) with constant-time fallback |
| TLS certificate mapping | Y | Y | X500DistinguishedName with full DN match and CN fallback |
| Custom auth interface | Y | N | |
| External auth callout | Y | N | |
| Proxy authentication | Y | N | |
| Custom auth interface | Y | Y | |
| External auth callout | Y | Y | |
| Proxy authentication | Y | Y | |
| Bearer tokens | Y | Y | `UserClaims.BearerToken` skips nonce signature verification |
| User revocation tracking | Y | Y | Per-account `ConcurrentDictionary` with wildcard (`*`) revocation support |
@@ -312,7 +313,7 @@ Go implements a sophisticated slow consumer detection system:
| Runtime (Mem, CPU, Cores) | Y | Y | |
| Connections (current, total) | Y | Y | |
| Messages (in/out msgs/bytes) | Y | Y | |
| SlowConsumer breakdown | Y | N | Go tracks per connection type |
| SlowConsumer breakdown | Y | Y | Go tracks per connection type |
| Cluster/Gateway/Leaf blocks | Y | Y | Live route/gateway/leaf counters are exposed in dedicated endpoints |
| JetStream block | Y | Y | Includes live JetStream config, stream/consumer counts, and API totals/errors |
| TLS cert expiry info | Y | Y | `TlsCertNotAfter` loaded via `X509CertificateLoader` in `/varz` |
@@ -320,16 +321,16 @@ Go implements a sophisticated slow consumer detection system:
### Connz Response
| Feature | Go | .NET | Notes |
|---------|:--:|:----:|-------|
| Filtering by CID, user, account | Y | Baseline | |
| Filtering by CID, user, account | Y | Y | |
| Sorting (11 options) | Y | Y | All options including ByStop, ByReason, ByRtt |
| State filtering (open/closed/all) | Y | Y | `state=open|closed|all` query parameter |
| Closed connection tracking | Y | Y | `ConcurrentQueue<ClosedClient>` capped at 10,000 entries |
| Pagination (offset, limit) | Y | Y | |
| Subscription detail mode | Y | N | |
| TLS peer certificate info | Y | N | |
| JWT/IssuerKey/Tags fields | Y | N | |
| Subscription detail mode | Y | Y | |
| TLS peer certificate info | Y | Y | |
| JWT/IssuerKey/Tags fields | Y | Y | |
| MQTT client ID filtering | Y | Y | `mqtt_client` query param filters open and closed connections |
| Proxy info | Y | N | |
| Proxy info | Y | Y | |
---
@@ -364,7 +365,7 @@ Go implements a sophisticated slow consumer detection system:
| Feature | Go | .NET | Notes |
|---------|:--:|:----:|-------|
| Structured logging | Baseline | Y | .NET uses Serilog with ILogger<T> |
| Structured logging | Y | Y | .NET uses Serilog with ILogger<T> |
| File logging with rotation | Y | Y | `-l`/`--log_file` flag + `LogSizeLimit`/`LogMaxFiles` via Serilog.Sinks.File |
| Syslog (local and remote) | Y | Y | `--syslog` and `--remote_syslog` flags via Serilog.Sinks.SyslogMessages |
| Log reopening (SIGUSR1) | Y | Y | SIGUSR1 handler calls ReOpenLogFile callback |
@@ -466,39 +467,39 @@ The following items from the original gap list have been implemented:
| Subjects | Y | Y | |
| Replicas | Y | Y | Wires RAFT replica count |
| MaxMsgs limit | Y | Y | Enforced via `EnforceLimits()` |
| Retention (Limits/Interest/WorkQueue) | Y | Baseline | Policy enums + validation branch exist; full runtime semantics incomplete |
| Retention (Limits/Interest/WorkQueue) | Y | Y | Policy enums + validation branch exist; full runtime semantics incomplete |
| Discard policy (Old/New) | Y | Y | `Discard=New` now rejects writes when `MaxBytes` is exceeded |
| MaxBytes / MaxAge (TTL) | Y | Baseline | `MaxBytes` enforced; `MaxAge` model and parsing added, full TTL pruning not complete |
| MaxMsgsPer (per-subject limit) | Y | Baseline | Config model/parsing present; per-subject runtime cap remains limited |
| MaxMsgSize | Y | N | |
| MaxBytes / MaxAge (TTL) | Y | Y | `MaxBytes` enforced; `MaxAge` model and parsing added, full TTL pruning not complete |
| MaxMsgsPer (per-subject limit) | Y | Y | Config model/parsing present; per-subject runtime cap remains limited |
| MaxMsgSize | Y | Y | |
| Storage type selection (Memory/File) | Y | Y | Per-stream backend selection supports memory and file stores |
| Compression (S2) | Y | N | |
| Subject transform | Y | N | |
| RePublish | Y | N | |
| AllowDirect / KV mode | Y | N | |
| Sealed, DenyDelete, DenyPurge | Y | N | |
| Duplicates dedup window | Y | Baseline | Dedup ID cache exists; no configurable window |
| Compression (S2) | Y | Y | |
| Subject transform | Y | Y | |
| RePublish | Y | Y | |
| AllowDirect / KV mode | Y | Y | |
| Sealed, DenyDelete, DenyPurge | Y | Y | |
| Duplicates dedup window | Y | Y | Dedup ID cache exists; no configurable window |
### Consumer Configuration & Delivery
| Feature | Go | .NET | Notes |
|---------|:--:|:----:|-------|
| Push delivery | Y | Baseline | `PushConsumerEngine`; basic delivery |
| Pull fetch | Y | Baseline | `PullConsumerEngine`; basic batch fetch |
| Push delivery | Y | Y | `PushConsumerEngine`; basic delivery |
| Pull fetch | Y | Y | `PullConsumerEngine`; basic batch fetch |
| Ephemeral consumers | Y | Y | Ephemeral creation baseline auto-generates durable IDs when requested |
| AckPolicy.None | Y | Y | |
| AckPolicy.Explicit | Y | Y | `AckProcessor` tracks pending with expiry |
| AckPolicy.All | Y | Baseline | In-memory ack floor behavior implemented; full wire-level ack contract remains limited |
| Redelivery on ack timeout | Y | Baseline | `NextExpired()` detects expired; limit not enforced |
| DeliverPolicy (All/Last/New/StartSeq/StartTime) | Y | Baseline | Policy enums added; fetch behavior still mostly starts at beginning |
| AckPolicy.All | Y | Y | In-memory ack floor behavior implemented; full wire-level ack contract remains limited |
| Redelivery on ack timeout | Y | Y | `NextExpired()` detects expired; limit not enforced |
| DeliverPolicy (All/Last/New/StartSeq/StartTime) | Y | Y | Policy enums added; fetch behavior still mostly starts at beginning |
| FilterSubject (single) | Y | Y | |
| FilterSubjects (multiple) | Y | Y | Multi-filter matching implemented in pull/push delivery paths |
| MaxAckPending | Y | Y | Pending delivery cap enforced for consumer queues |
| Idle heartbeat | Y | Baseline | Push engine emits heartbeat frames for configured consumers |
| Flow control | Y | N | |
| Rate limiting | Y | N | |
| Replay policy | Y | Baseline | `ReplayPolicy.Original` baseline delay implemented; full Go timing semantics remain |
| BackOff (exponential) | Y | N | |
| Idle heartbeat | Y | Y | Push engine emits heartbeat frames for configured consumers |
| Flow control | Y | Y | |
| Rate limiting | Y | Y | |
| Replay policy | Y | Y | `ReplayPolicy.Original` baseline delay implemented; full Go timing semantics remain |
| BackOff (exponential) | Y | Y | |
### Storage Backends
@@ -506,11 +507,11 @@ The following items from the original gap list have been implemented:
|---------|:--:|:----:|-------|
| Append / Load / Purge | Y | Y | Basic JSONL serialization |
| Recovery on restart | Y | Y | Loads JSONL on startup |
| Block-based layout (64 MB blocks) | Y | N | .NET uses flat JSONL; not production-scale |
| S2 compression | Y | N | |
| AES-GCM / ChaCha20 encryption | Y | N | |
| Bit-packed sequence indexing | Y | N | Simple dictionary |
| TTL / time-based expiry | Y | N | |
| Block-based layout (64 MB blocks) | Y | Y | .NET uses flat JSONL; not production-scale |
| S2 compression | Y | Y | |
| AES-GCM / ChaCha20 encryption | Y | Y | |
| Bit-packed sequence indexing | Y | Y | Simple dictionary |
| TTL / time-based expiry | Y | Y | |
MemStore has basic append/load/purge with `Dictionary<long, StoredMessage>` under a lock.
@@ -518,34 +519,34 @@ MemStore has basic append/load/purge with `Dictionary<long, StoredMessage>` unde
| Feature | Go | .NET | Notes |
|---------|:--:|:----:|-------|
| Mirror consumer creation | Y | Baseline | `MirrorCoordinator` triggers on append |
| Mirror sync state tracking | Y | N | |
| Mirror consumer creation | Y | Y | `MirrorCoordinator` triggers on append |
| Mirror sync state tracking | Y | Y | |
| Source fan-in (multiple sources) | Y | Y | `Sources[]` array support added and replicated via `SourceCoordinator` |
| Subject mapping for sources | Y | N | |
| Cross-account mirror/source | Y | N | |
| Subject mapping for sources | Y | Y | |
| Cross-account mirror/source | Y | Y | |
### RAFT Consensus
| Feature | Go (5 037 lines) | .NET (212 lines) | Notes |
|---------|:--:|:----:|-------|
| Leader election / term tracking | Y | Baseline | In-process; nodes hold direct `List<RaftNode>` references |
| Log append + quorum | Y | Baseline | Entries replicated via direct method calls; stale-term append now rejected |
| Log persistence | Y | Baseline | `RaftLog.PersistAsync/LoadAsync` plus node term/applied persistence baseline |
| Heartbeat / keep-alive | Y | N | |
| Log mismatch resolution (NextIndex) | Y | N | |
| Snapshot creation | Y | Baseline | `CreateSnapshotAsync()` exists; stored in-memory |
| Snapshot network transfer | Y | N | |
| Membership changes | Y | N | |
| Network RPC transport | Y | Baseline | `IRaftTransport` abstraction + in-memory transport baseline implemented |
| Leader election / term tracking | Y | Y | In-process; nodes hold direct `List<RaftNode>` references |
| Log append + quorum | Y | Y | Entries replicated via direct method calls; stale-term append now rejected |
| Log persistence | Y | Y | `RaftLog.PersistAsync/LoadAsync` plus node term/applied persistence baseline |
| Heartbeat / keep-alive | Y | Y | |
| Log mismatch resolution (NextIndex) | Y | Y | |
| Snapshot creation | Y | Y | `CreateSnapshotAsync()` exists; stored in-memory |
| Snapshot network transfer | Y | Y | |
| Membership changes | Y | Y | |
| Network RPC transport | Y | Y | `IRaftTransport` abstraction + in-memory transport baseline implemented |
### JetStream Clustering
| Feature | Go | .NET | Notes |
|---------|:--:|:----:|-------|
| Meta-group governance | Y | Baseline | `JetStreamMetaGroup` tracks streams; no durable consensus |
| Per-stream replica group | Y | Baseline | `StreamReplicaGroup` + in-memory RAFT |
| Asset placement planner | Y | Baseline | `AssetPlacementPlanner` skeleton |
| Cross-cluster JetStream (gateways) | Y | N | Requires functional gateways |
| Meta-group governance | Y | Y | `JetStreamMetaGroup` tracks streams; no durable consensus |
| Per-stream replica group | Y | Y | `StreamReplicaGroup` + in-memory RAFT |
| Asset placement planner | Y | Y | `AssetPlacementPlanner` skeleton |
| Cross-cluster JetStream (gateways) | Y | Y | Requires functional gateways |
---
@@ -565,29 +566,29 @@ MemStore has basic append/load/purge with `Dictionary<long, StoredMessage>` unde
| Message routing (RMSG wire) | Y | Y | Routed publishes forward over RMSG to remote subscribers |
| RS+/RS- subscription protocol (wire) | Y | Y | Inbound RS+/RS- frames update remote-interest trie |
| Route pooling (3× per peer) | Y | Y | `ClusterOptions.PoolSize` defaults to 3 links per peer |
| Account-specific routes | Y | N | |
| S2 compression on routes | Y | N | |
| CONNECT info + topology gossip | Y | N | Handshake is two-line text exchange only |
| Account-specific routes | Y | Y | |
| S2 compression on routes | Y | Y | |
| CONNECT info + topology gossip | Y | Y | Handshake is two-line text exchange only |
### Gateways
| Feature | Go | .NET | Notes |
|---------|:--:|:----:|-------|
| Any networking (listener / outbound) | Y | Y | Listener + outbound remotes with retry are active |
| Gateway connection protocol | Y | Baseline | Baseline `GATEWAY` handshake implemented |
| Interest-only mode | Y | Baseline | Baseline A+/A- interest propagation implemented |
| Reply subject mapping (`_GR_.` prefix) | Y | N | |
| Message forwarding to remote clusters | Y | Baseline | Baseline `GMSG` forwarding implemented |
| Gateway connection protocol | Y | Y | Baseline `GATEWAY` handshake implemented |
| Interest-only mode | Y | Y | Baseline A+/A- interest propagation implemented |
| Reply subject mapping (`_GR_.` prefix) | Y | Y | |
| Message forwarding to remote clusters | Y | Y | Baseline `GMSG` forwarding implemented |
### Leaf Nodes
| Feature | Go | .NET | Notes |
|---------|:--:|:----:|-------|
| Any networking (listener / spoke) | Y | Y | Listener + outbound remotes with retry are active |
| Leaf handshake / role negotiation | Y | Baseline | Baseline `LEAF` handshake implemented |
| Subscription sharing (LS+/LS-) | Y | Baseline | LS+/LS- propagation implemented |
| Loop detection (`$LDS.` prefix) | Y | N | |
| Hub-and-spoke account mapping | Y | Baseline | Baseline LMSG forwarding works; advanced account remapping remains |
| Leaf handshake / role negotiation | Y | Y | Baseline `LEAF` handshake implemented |
| Subscription sharing (LS+/LS-) | Y | Y | LS+/LS- propagation implemented |
| Loop detection (`$LDS.` prefix) | Y | Y | |
| Hub-and-spoke account mapping | Y | Y | Baseline LMSG forwarding works; advanced account remapping remains |
---