Files
natsdotnet/differences.md
Joseph Doherty 3941c85e76 Merge branch 'feature/core-lifecycle' into main
Reconcile close reason tracking: feature branch's MarkClosed() and
ShouldSkipFlush/FlushAndCloseAsync now use main's ClientClosedReason
enum. ClosedState enum retained for forward compatibility.
2026-02-23 00:09:30 -05:00

17 KiB

Go vs .NET NATS Server: Functionality Differences

Excludes clustering/routes, gateways, leaf nodes, and JetStream. Generated 2026-02-22 by comparing golang/nats-server/server/ against src/NATS.Server/.


1. Core Server Lifecycle

Server Initialization

Feature Go .NET Notes
NKey generation (server identity) Y Y Ed25519 key pair via NATS.NKeys at startup
System account setup Y Y $SYS account created; no event publishing yet (stub)
Config file validation on startup Y Stub -c flag parsed, ConfigFile stored, but no config parser
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
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
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
Random/ephemeral port (port=0) Y Y Port resolved after Bind+Listen, stored in _options.Port

Shutdown

Feature Go .NET Notes
Graceful shutdown with WaitForShutdown() Y Y Idempotent CAS-guarded ShutdownAsync() + blocking WaitForShutdown()
Close reason tracking per connection Y Y 37-value ClosedState enum, CAS-based first-writer-wins MarkClosed()
Lame duck mode (stop new, drain existing) Y Y LameDuckShutdownAsync() with grace period + stagger-close with jitter
Wait for accept loop completion Y Y TaskCompletionSource signaled in accept loop finally
Flush pending data before close Y Y FlushAndCloseAsync() with best-effort flush, skip-flush for error conditions

Signal Handling

Signal Go .NET Notes
SIGINT (Ctrl+C) Y Y Both handle graceful shutdown
SIGTERM Y Y PosixSignalRegistration triggers ShutdownAsync()
SIGUSR1 (reopen logs) Y Stub Signal registered, handler logs "not yet implemented"
SIGUSR2 (lame duck mode) Y Y Triggers LameDuckShutdownAsync()
SIGHUP (config reload) Y Stub Signal registered, handler logs "not yet implemented"
Windows Service integration Y N

2. Client / Connection Handling

Concurrency Model

Feature Go .NET Notes
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

Connection Types

Type Go .NET Notes
CLIENT Y Y
ROUTER Y N Excluded per scope
GATEWAY Y N Excluded per scope
LEAF Y N Excluded per scope
SYSTEM (internal) Y N
JETSTREAM (internal) Y N
ACCOUNT (internal) Y N
WebSocket clients Y N
MQTT clients Y N

Client Features

Feature Go .NET Notes
Echo suppression (echo: false) Y Y .NET checks echo in delivery path (NatsServer.cs:234,253)
Verbose mode (+OK responses) Y Y Sends +OK after CONNECT, SUB, UNSUB, PUB when verbose:true
No-responders validation Y Y CONNECT rejects no_responders without headers; 503 HMSG on no match
Slow consumer detection Y Y Pending bytes threshold (64MB) + write deadline timeout (10s)
Write deadline / timeout policies Y Y WriteDeadline option with CancellationTokenSource.CancelAfter on flush
RTT measurement Y N Go tracks round-trip time per client
Per-client trace mode Y N
Detailed close reason tracking Y Y 37-value ClosedState enum with CAS-based MarkClosed()
Connection state flags (16 flags) Y Y 7-flag ClientFlagHolder with Interlocked.Or/And

Slow Consumer Handling

Go implements a sophisticated slow consumer detection system:

  • Tracks pendingBytes per client output buffer
  • If pending exceeds maxPending, enters stall mode (2-5ms waits)
  • Total stall capped at 10ms per read cycle
  • Closes with SlowConsumerPendingBytes or SlowConsumerWriteDeadline
  • Sets isSlowConsumer flag for monitoring

.NET now implements pending bytes tracking and write deadline enforcement via Channel<ReadOnlyMemory<byte>>. Key differences from Go: no stall/retry mode (immediate close on threshold exceeded), write deadline via CancellationTokenSource.CancelAfter instead of SetWriteDeadline. IsSlowConsumer flag and server-level SlowConsumerCount stats are tracked for monitoring.

Stats Tracking

Feature Go .NET Notes
Per-connection atomic stats Y Y .NET uses Interlocked for stats access
Per-read-cycle stat batching Y Y Local accumulators flushed via Interlocked.Add per read cycle
Per-account stats Y N
Slow consumer counters Y Y SlowConsumers and SlowConsumerClients incremented on detection

3. Protocol Parsing

Parser Architecture

Aspect Go .NET
Approach Byte-by-byte state machine (74 states) Two-phase: line extraction + command dispatch
Case handling Per-state character checks Bit-mask lowercase normalization (| 0x20)
Buffer strategy Jump-ahead optimization for payloads Direct size-based reads via Pipe
Split-buffer handling argBuf accumulation with scratch buffer State variables (_awaitingPayload, etc.)
Error model Inline error sending + error return Exception-based (ProtocolViolationException)
CRLF in payload Included in message buffer Excluded by design

Protocol Operations

Operation Go .NET Notes
PUB Y Y
HPUB (headers) Y Y
SUB Y Y
UNSUB Y Y
CONNECT Y Y
INFO Y Y
PING / PONG Y Y
MSG / HMSG Y Y
+OK / -ERR Y Y
RS+/RS-/RMSG (routes) Y N Excluded per scope
A+/A- (accounts) Y N Excluded per scope
LS+/LS-/LMSG (leaf) Y N Excluded per scope

Protocol Parsing Gaps

Feature Go .NET Notes
Multi-client-type command routing Y N Go checks c.kind to allow/reject commands
Protocol tracing in parser Y N Go calls traceInOp() per operation
Subject mapping (input→output) Y N Go transforms subjects via mapping rules
MIME header parsing Y N .NET delegates header handling to client layer
Message trace event initialization Y N

Protocol Writing

Aspect Go .NET Notes
INFO serialization Once at startup Every send .NET re-serializes JSON each time
MSG/HMSG construction Direct buffer write String interpolation → byte encode More allocations in .NET
Pre-encoded constants Y Y Both pre-encode PING/PONG/OK

4. Subscriptions & Subject Matching

Trie Implementation

Feature Go .NET Notes
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
Atomic generation ID for invalidation Y N .NET clears cache explicitly
Cache eviction strategy Random First-N Semantic difference minimal

Missing SubList Features

Feature Go .NET Notes
Stats() — comprehensive statistics Y N Matches, cache hits, inserts, removes, fanout
HasInterest() — fast bool check Y N
NumInterest() — fast count Y N
ReverseMatch() — pattern→literal query Y N
RemoveBatch() — efficient bulk removal Y N
All() — enumerate all subscriptions Y N
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

Subject Validation

Feature Go .NET Notes
Basic validation (empty tokens, wildcards) Y Y
Literal subject check Y Y
UTF-8/null rune validation Y N Go has checkRunes parameter
Collision detection (SubjectsCollide) Y N
Token utilities (tokenAt, numTokens) Y N
Stack-allocated token buffer Y N Go uses [32]string{} on stack

Subscription Lifecycle

Feature Go .NET Notes
Per-account subscription limit Y N
Auto-unsubscribe on max messages Y Y .NET enforces at delivery time (NatsServer.cs:269-270)
Subscription routing propagation Y N For clusters
Queue weight (qw) field Y N For remote queue load balancing

5. Authentication & Authorization

Auth Mechanisms

Mechanism Go .NET Notes
Username/password Y Y
Token Y Y
NKeys (Ed25519) Y Y .NET has framework but integration is basic
JWT validation Y N
Bcrypt password hashing Y Y .NET supports bcrypt ($2* prefix) with constant-time fallback
TLS certificate mapping Y N Property exists but no implementation
Custom auth interface Y N
External auth callout Y N
Proxy authentication Y N
Bearer tokens Y N
User revocation tracking Y N

Account System

Feature Go .NET Notes
Per-account SubList isolation Y Y
Multi-account user resolution Y N .NET has basic account, no resolution
Account exports/imports Y N
Per-account connection limits Y N
Per-account subscription limits Y N
Account JetStream limits Y N Excluded per scope

Permissions

Feature Go .NET Notes
Publish allow list Y Y
Subscribe allow list Y Y
Publish deny list Y Partial .NET has deny in struct but limited enforcement
Subscribe deny list Y Partial Same
Message-level deny filtering Y N Go filters at delivery time
Permission caching (128 entries) Y N
Response permissions (reply tracking) Y N Dynamic reply subject authorization
Permission templates (JWT) Y N e.g., {{name()}}, {{account-tag(...)}}

6. Configuration

CLI Flags

Flag Go .NET Notes
-p/--port Y Y
-a/--addr Y Y
-n/--name (ServerName) Y Y
-m/--http_port (monitoring) Y Y
-c (config file) Y Stub Flag parsed, stored in ConfigFile, no config parser
-D/-V/-DV (debug/trace) Y N
--tlscert/--tlskey/--tlscacert Y Y
--tlsverify Y Y
--http_base_path Y Y
--https_port Y Y

Configuration System

Feature Go .NET Notes
Config file parsing Y N Go has custom conf parser with includes
Hot reload (SIGHUP) Y N
Config change detection Y N Go tracks inConfig/inCmdLine origins
~450 option fields Y ~54 .NET covers core options only

Missing Options Categories

  • Logging options (file, rotation, syslog, trace levels)
  • Advanced limits (MaxSubs, MaxSubTokens, MaxPending, WriteDeadline)
  • Tags/metadata
  • OCSP configuration
  • WebSocket/MQTT options
  • Operator mode / account resolver

7. Monitoring

HTTP Endpoints

Endpoint Go .NET Notes
/healthz Y Y
/varz Y Y
/connz Y Y
/ (root listing) Y Y
/routez Y Stub Returns empty response
/gatewayz Y Stub Returns empty response
/leafz Y Stub Returns empty response
/subz / /subscriptionsz Y Stub Returns empty response
/accountz Y Stub Returns empty response
/accstatz Y Stub Returns empty response
/jsz Y Stub Returns empty response

Varz Response

Field Category Go .NET Notes
Identity (ID, Name, Version) Y Y
Network (Host, Port, URLs) Y Y
Security (AuthRequired, TLS) Y Y
Limits (MaxConn, MaxPayload) Y Y
Timing (Start, Now, Uptime) Y Y
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
Cluster/Gateway/Leaf blocks Y N Excluded per scope
JetStream block Y N Excluded per scope
TLS cert expiry info Y N

Connz Response

Feature Go .NET Notes
Filtering by CID, user, account Y Partial
Sorting (11 options) Y Y .NET missing ByStop, ByReason
Pagination (offset, limit) Y Y
Subscription detail mode Y N
TLS peer certificate info Y N
JWT/IssuerKey/Tags fields Y N
MQTT client ID filtering Y N
Proxy info Y N

8. TLS

TLS Modes

Mode Go .NET Notes
No TLS Y Y
INFO-first (default NATS) Y Y
TLS-first (before INFO) Y Y
Mixed/Fallback Y Y
TLS-required Y Y

TLS Features

Feature Go .NET Notes
PEM cert/key loading Y Y
CA chain validation Y Y
Mutual TLS (client certs) Y Y
Certificate pinning (SHA256 SPKI) Y Y
TLS handshake timeout Y Y
TLS rate limiting Y Property only .NET has the option but enforcement is partial
First-byte peeking (0x16 detection) Y Y
Cert subject→user mapping Y N TlsMap property exists, no implementation
OCSP stapling Y N
Min TLS version control Y Y

9. Logging

Feature Go .NET Notes
Structured logging Partial Y .NET uses Serilog with ILogger
File logging with rotation Y N
Syslog (local and remote) Y N
Log reopening (SIGUSR1) Y N
Trace mode (protocol-level) Y N
Debug mode Y N
Per-subsystem log control Y N
Color output on TTY Y N
Timestamp format control Y N

10. Ping/Pong & Keepalive

Feature Go .NET Notes
Server-initiated PING Y Y
Configurable interval Y Y PingInterval option
Max pings out Y Y MaxPingsOut option
Stale connection close Y Y
RTT-based first PING delay Y N Go delays first PING based on RTT
RTT tracking Y N
Stale connection watcher Y N Go has dedicated watcher goroutine

Summary: Critical Gaps for Production Use

High Priority

  1. Slow consumer detection — unbounded writes can exhaust memory (stat fields exist but no detection logic)
  2. Write coalescing / batch flush — performance gap for high-throughput scenarios

Medium Priority

  1. Verbose mode — clients expect +OK when verbose: true
  2. Permission deny enforcement at delivery — deny lists checked at SUB/PUB time but not during message delivery
  3. Config file parsing — needed for production deployment (CLI stub exists)
  4. Hot reload — needed for zero-downtime config changes (SIGHUP stub exists)
  5. File logging with rotation — needed for production logging
  6. No-responders validation — flag parsed but not enforced

Lower Priority

  1. Dynamic buffer sizing — delegated to Pipe, less optimized for long-lived connections
  2. JWT authentication — needed for operator mode
  3. TLS certificate mapping — property exists, not implemented
  4. OCSP support — certificate revocation checking
  5. Subject mapping — input→output subject transformation
  6. Protocol tracing — no trace-level logging
  7. Subscription statistics — SubList has no stats collection
  8. Per-account limits — connections, subscriptions per account
  9. Reply subject tracking — dynamic response permissions
  10. Windows Service integration — needed for Windows deployment