Files
natsdotnet/gaps/routes.md
2026-02-25 15:12:52 -05:00

18 KiB
Raw Blame History

Routes — Gap Analysis

This file tracks what has and hasn't been ported from Go to .NET for the Routes module. See stillmissing.md for the full LOC comparison across all modules.

LLM Instructions: How to Analyze This Category

Step 1: Read the Go Reference Files

Read each Go source file listed below. For every file:

  1. Extract all exported types (structs, interfaces, type aliases)
  2. Extract all exported methods on those types (receiver functions)
  3. Extract all exported standalone functions
  4. Note key constants, enums, and protocol states
  5. Note important unexported helpers that implement core logic (functions >20 lines)
  6. Pay attention to concurrency patterns (goroutines, mutexes, channels) — these map to different .NET patterns

Step 2: Read the .NET Implementation Files

Read all .cs files in the .NET directories listed below. For each Go symbol found in Step 1:

  1. Search for a matching type, method, or function in .NET
  2. If found, compare the behavior: does it handle the same edge cases? Same error paths?
  3. If partially implemented, note what's missing
  4. If not found, note it as MISSING

Step 3: Cross-Reference Tests

Compare Go test functions against .NET test methods:

  1. For each Go Test* function, check if a corresponding .NET [Fact] or [Theory] exists
  2. Note which test scenarios are covered and which are missing
  3. Check the parity DB (docs/test_parity.db) for existing mappings:
    sqlite3 docs/test_parity.db "SELECT go_test, dotnet_test, confidence FROM test_mappings tm JOIN go_tests gt ON tm.go_test_id=gt.rowid JOIN dotnet_tests dt ON tm.dotnet_test_id=dt.rowid WHERE gt.go_file LIKE '%PATTERN%'"
    

Step 4: Classify Each Item

Use these status values:

Status Meaning
PORTED Equivalent exists in .NET with matching behavior
PARTIAL .NET implementation exists but is incomplete (missing edge cases, error handling, or features)
MISSING No .NET equivalent found — needs to be ported
NOT_APPLICABLE Go-specific pattern that doesn't apply to .NET (build tags, platform-specific goroutine tricks, etc.)
DEFERRED Intentionally skipped for now (document why)

Step 5: Fill In the Gap Inventory

Add rows to the Gap Inventory table below. Group by Go source file. Include the Go file and line number so a porting LLM can jump directly to the reference implementation.

Key Porting Notes for Routes

  • Route connections use the same client.go read/write loop but with ClientKind = ROUTER.
  • Route pooling sends different accounts over different connections to avoid head-of-line blocking.
  • RS+/RS- are subscription interest propagation messages between clustered servers.
  • RMSG is the routed message format (differs from client MSG).

Go Reference Files (Source)

  • golang/nats-server/server/route.go — Full-mesh cluster routes (~3,300 lines). Route pooling (default 3 connections per peer). Account-specific dedicated routes. Protocol: RS+/RS- for subscribe propagation, RMSG for routed messages.

Go Reference Files (Tests)

  • golang/nats-server/server/routes_test.go
  • golang/nats-server/test/routes_test.go (integration)
  • golang/nats-server/test/new_routes_test.go (integration)

.NET Implementation Files (Source)

  • src/NATS.Server/Routes/ (all files)

.NET Implementation Files (Tests)

  • tests/NATS.Server.Tests/Routes/

Gap Inventory

golang/nats-server/server/route.go

Go Symbol Go File:Line Status .NET Equivalent Notes
RouteType (type alias + consts Implicit/Explicit) route.go:3644 PORTED src/NATS.Server/Routes/RouteConnection.cs — implicit/explicit distinction tracked in RouteConnection handshake and RouteManager.ConnectToRouteWithRetryAsync No explicit enum; Implicit/Explicit distinction is encoded in how routes are established (solicited vs inbound)
route struct (unexported) route.go:5694 PARTIAL src/NATS.Server/Routes/RouteConnection.cs:8 Fields remoteID, poolIdx, accName, noPool, compression, gossipMode are present. Fields for lnoc, lnocu, jetstream, connectURLs, wsConnURLs, gatewayURL, leafnodeURL, hash, idHash, startNewRoute, retry are MISSING — not modelled in .NET
routeInfo struct (unexported) route.go:97101 MISSING Used internally for deferred pool-connection creation after first PONG; no .NET equivalent
gossipDefault/gossipDisabled/gossipOverride consts route.go:104108 MISSING Gossip mode bytes used in INFO propagation; not implemented in .NET
connectInfo struct route.go:110124 PARTIAL src/NATS.Server/Routes/RouteConnection.cs:328 (BuildConnectInfoJson) .NET builds a simplified JSON payload; connectInfo fields Echo, Verbose, Pedantic, TLS, Headers, Cluster, Dynamic, LNOC, LNOCU are all MISSING from the .NET payload
ConProto/InfoProto protocol format strings route.go:127130 PARTIAL src/NATS.Server/Routes/RouteConnection.cs:73,83 Handshake uses a simplified ROUTE <serverId> format rather than CONNECT <json> / INFO <json>
clusterTLSInsecureWarning const route.go:134 NOT_APPLICABLE TLS not yet implemented in .NET port; warning string has no counterpart
defaultRouteMaxPingInterval const route.go:140 MISSING Ping interval management for compression RTT auto-mode not implemented
routeConnectDelay/routeConnectMaxDelay/routeMaxPingInterval vars route.go:145148 PARTIAL src/NATS.Server/Routes/RouteManager.cs:486 (250ms hardcoded delay) .NET hardcodes 250ms retry delay; Go uses configurable DEFAULT_ROUTE_CONNECT with exponential backoff
(c *client) removeReplySub route.go:151 MISSING Reply-sub cleanup for remote reply subs not implemented
(c *client) processAccountSub route.go:167 NOT_APPLICABLE Gateway-only path; gateway sub interest not in routes module
(c *client) processAccountUnsub route.go:174 NOT_APPLICABLE Gateway-only path
(c *client) processRoutedOriginClusterMsgArgs route.go:182 MISSING LMSG (origin-cluster routed msg) arg parsing not implemented; .NET only handles RMSG
(c *client) processRoutedHeaderMsgArgs route.go:281 MISSING HMSG (header msg from route) arg parsing not implemented
(c *client) processRoutedMsgArgs route.go:378 PARTIAL src/NATS.Server/Routes/RouteConnection.cs:187222 (ReadFramesAsync) .NET parses basic RMSG account/subject/reply/size. Missing: queue-group routing indicators (+/`
(c *client) processInboundRoutedMsg route.go:460 PARTIAL src/NATS.Server/Routes/RouteConnection.cs:219221 .NET fires RoutedMessageReceived callback; missing: stats update, gateway reply handling (handleGatewayReply), account lookup and fanout via processMsgResults
(c *client) sendRouteConnect route.go:503 MISSING Outbound CONNECT protocol (with cluster auth, TLS flags, etc.) not sent; .NET uses a simpler ROUTE <serverId> handshake
computeRoutePoolIdx route.go:538 PORTED src/NATS.Server/Routes/RouteManager.cs:149 (ComputeRoutePoolIdx) FNV-1a 32-bit hash, identical algorithm
(c *client) processRouteInfo route.go:549 MISSING Full INFO processing (cluster name negotiation, compression negotiation, duplicate detection, account route setup) not implemented; .NET handshake is a simple ID exchange
(s *Server) negotiateRouteCompression route.go:897 PARTIAL src/NATS.Server/Routes/RouteCompressionCodec.cs:82 (NegotiateCompression) .NET has the negotiation logic; but integration into handshake (INFO exchange, switching compression writer/reader mid-stream) is MISSING
(s *Server) updateRemoteRoutePerms route.go:953 MISSING Route permission update on INFO reload not implemented
(s *Server) sendAsyncInfoToClients route.go:1015 MISSING Async INFO broadcast to connected clients not implemented
(s *Server) processImplicitRoute route.go:1043 PARTIAL src/NATS.Server/Routes/RouteManager.cs:107 (ProcessImplicitRoute) .NET collects discovered URLs; missing: duplicate-ID check, pinned-account re-solicitation, hasThisRouteConfigured guard
(s *Server) hasThisRouteConfigured route.go:1104 MISSING Check whether incoming gossip URL is already a configured explicit route; not implemented
(s *Server) forwardNewRouteInfoToKnownServers route.go:1139 PARTIAL src/NATS.Server/Routes/RouteManager.cs:127 (ForwardNewRouteInfoToKnownServers) .NET raises an event with the new peer URL; missing: gossip mode logic (gossipDefault/gossipDisabled/gossipOverride), pinned-account route filtering, serialized INFO JSON sending
(c *client) canImport route.go:1226 MISSING Route import permission check not implemented
(c *client) canExport route.go:1235 MISSING Route export permission check not implemented
(c *client) setRoutePermissions route.go:1244 MISSING Route permission mapping (Import→Publish, Export→Subscribe) not implemented
asubs struct route.go:1263 NOT_APPLICABLE Internal Go helper to group subscriptions by account during cleanup; .NET uses LINQ equivalents
getAccNameFromRoutedSubKey route.go:1273 MISSING Sub key parsing for account name extraction not implemented
(c *client) getRoutedSubKeyInfo route.go:1290 MISSING Helper to determine account/key info for a route's subscriptions; not implemented
(c *client) removeRemoteSubs route.go:1299 PARTIAL src/NATS.Server/Routes/RouteManager.cs:577 (RemoveRoute) .NET removes the route connection but does NOT remove individual remote subscriptions from the SubList on close
(c *client) removeRemoteSubsForAcc route.go:1352 MISSING Per-account remote sub removal for dedicated route transition not implemented
(c *client) parseUnsubProto route.go:1366 MISSING RS-/LS- protocol arg parser not implemented; .NET ReadFramesAsync only extracts account/subject/queue loosely
(c *client) processRemoteUnsub route.go:1404 PARTIAL src/NATS.Server/Routes/RouteConnection.cs:177185 .NET fires RemoteSubscriptionReceived with IsRemoval=true; missing: sub key lookup and removal from SubList, gateway/leafnode interest updates
(c *client) processRemoteSub route.go:1489 PARTIAL src/NATS.Server/Routes/RouteConnection.cs:167175 .NET fires RemoteSubscriptionReceived; missing: key construction with type byte prefix, account lookup/creation, permission check (canExport), SubList insertion, gateway/leafnode updates, queue-weight delta tracking
(c *client) addRouteSubOrUnsubProtoToBuf route.go:1729 PARTIAL src/NATS.Server/Routes/RouteConnection.cs:95109 (SendRsPlusAsync/SendRsMinusAsync) .NET sends RS+/RS- with account and optional queue; missing: LS+/LS- variant for leaf origin clusters, queue weight field in RS+
(s *Server) sendSubsToRoute route.go:1781 MISSING Bulk send of local subscription interest to newly connected route not implemented; .NET only propagates incremental sub/unsub
(c *client) sendRouteSubProtos route.go:1881 MISSING Batch RS+ send not implemented
(c *client) sendRouteUnSubProtos route.go:1890 MISSING Batch RS- send not implemented
(c *client) sendRouteSubOrUnSubProtos route.go:1898 MISSING Low-level batch RS+/RS-/LS+/LS- sender not implemented
(s *Server) createRoute route.go:1935 PARTIAL src/NATS.Server/Routes/RouteManager.cs:447,462 (HandleInboundRouteAsync/ConnectToRouteWithRetryAsync) .NET creates a RouteConnection and performs handshake; missing: TLS setup, auth timeout timer, CONNECT protocol sending, INFO JSON sending, compression negotiation, ping timer
routeShouldDelayInfo route.go:2082 MISSING Logic to delay initial INFO until pool connection auth is confirmed not implemented
(s *Server) generateRouteInitialInfoJSON route.go:2090 MISSING Route INFO JSON generation (with nonce, pool index, gossip mode, compression) not implemented
(s *Server) addRoute route.go:2113 PARTIAL src/NATS.Server/Routes/RouteManager.cs:496 (Register) .NET registers route in dictionary; missing: pool index management, duplicate detection with handleDuplicateRoute, per-account route registration in accRoutes, sendSubsToRoute call, gateway/leafnode URL propagation, forwardNewRouteInfoToKnownServers
hasSolicitedRoute route.go:2438 MISSING Helper to find a solicited route in a pool slice; not implemented
upgradeRouteToSolicited route.go:2458 MISSING Upgrade an inbound route to solicited status; not implemented
handleDuplicateRoute route.go:2473 MISSING Duplicate route resolution (close extra connection, preserve retry flag) not implemented
(c *client) importFilter route.go:2510 MISSING Permission-based subscription filter for sending to routes not implemented
(s *Server) updateRouteSubscriptionMap route.go:2519 PARTIAL src/NATS.Server/Routes/RouteManager.cs:381,392 (PropagateLocalSubscription/PropagateLocalUnsubscription) .NET broadcasts RS+/RS- to all routes; missing: account routePoolIdx-based routing, queue-weight dedup (sqmu/lqws), no-pool route handling, gateway/leafnode interest updates
(s *Server) startRouteAcceptLoop route.go:2696 PARTIAL src/NATS.Server/Routes/RouteManager.cs:333 (StartAsync) .NET binds and starts accept loop, solicits configured routes; missing: cluster name logging, TLS config on accept, routeInfo construction, advertise/NoAdvertise, LeafNode/Gateway URL propagation
(s *Server) setRouteInfoHostPortAndIP route.go:2829 MISSING Route INFO host/port/IP with Cluster.Advertise support not implemented
(s *Server) StartRouting route.go:2849 PORTED src/NATS.Server/Routes/RouteManager.cs:333 (StartAsync) Functionally equivalent: starts accept loop and solicits routes
(s *Server) reConnectToRoute route.go:2861 PARTIAL src/NATS.Server/Routes/RouteManager.cs:462 (ConnectToRouteWithRetryAsync) .NET retries indefinitely with 250ms delay; missing: random jitter delay, explicit vs implicit distinction affecting delay, quit-channel integration
(s *Server) routeStillValid route.go:2881 MISSING Check that a route URL is still in configured routes list (for reconnect guard) not implemented
(s *Server) connectToRoute route.go:2890 PARTIAL src/NATS.Server/Routes/RouteManager.cs:462 (ConnectToRouteWithRetryAsync) .NET connects and retries; missing: explicit/implicit distinction, ConnectRetries limit, exponential backoff (ConnectBackoff), routesToSelf exclusion, address randomization from DNS
(c *client) isSolicitedRoute route.go:2976 MISSING Helper predicate; not implemented
(s *Server) saveRouteTLSName route.go:2985 NOT_APPLICABLE TLS not yet implemented in .NET port
(s *Server) solicitRoutes route.go:2996 PARTIAL src/NATS.Server/Routes/RouteManager.cs:347354 .NET solicits configured routes with pool connections; missing: per-account (pinned) route solicitation, saveRouteTLSName
(c *client) processRouteConnect route.go:3011 MISSING Parsing and validation of inbound CONNECT from route (cluster name check, wrong-port detection, LNOC/LNOCU flags) not implemented; .NET uses a simpler handshake
(s *Server) removeAllRoutesExcept route.go:3085 PORTED src/NATS.Server/Routes/RouteManager.cs:602 (RemoveAllRoutesExcept) Equivalent behavior: remove all routes not in the keep-set
(s *Server) removeRoute route.go:3113 PARTIAL src/NATS.Server/Routes/RouteManager.cs:577 (RemoveRoute) .NET removes from _routes dict; missing: per-account route cleanup (accRoutes), hash removal, gateway/leafnode URL withdrawal, noPool counter, reconnect-after-noPool logic
(s *Server) isDuplicateServerName route.go:3233 MISSING Duplicate server name detection across routes not implemented
(s *Server) forEachNonPerAccountRoute route.go:3263 NOT_APPLICABLE Internal Go iterator over route slice; .NET uses _routes.Values LINQ directly
(s *Server) forEachRoute route.go:3277 NOT_APPLICABLE Internal Go iterator; .NET enumerates _routes and _accountRoutes directly
(s *Server) forEachRouteIdx route.go:3292 NOT_APPLICABLE Internal Go pool-index iterator; .NET ComputeRoutePoolIdx achieves equivalent selection
(s *Server) forEachRemote route.go:3305 NOT_APPLICABLE Internal Go iterator (first non-nil per remote); .NET has no equivalent but uses LINQ

Keeping This File Updated

After porting work is completed:

  1. Update status: Change MISSING → PORTED or PARTIAL → PORTED for each item completed
  2. Add .NET path: Fill in the ".NET Equivalent" column with the actual file:line
  3. Re-count LOC: Update the LOC numbers in stillmissing.md:
    # Re-count .NET source LOC for this module
    find src/NATS.Server/Routes/ -name '*.cs' -type f -exec cat {} + | wc -l
    # Re-count .NET test LOC for this module
    find tests/NATS.Server.Tests/Routes/ -name '*.cs' -type f -exec cat {} + | wc -l
    
  4. Add a changelog entry below with date and summary of what was ported
  5. Update the parity DB if new test mappings were created:
    sqlite3 docs/test_parity.db "INSERT INTO test_mappings (go_test_id, dotnet_test_id, confidence, notes) VALUES (?, ?, 'manual', 'ported in YYYY-MM-DD session')"
    

Change Log

Date Change By
2026-02-25 File created with LLM analysis instructions auto
2026-02-25 Full gap inventory populated: 57 Go symbols classified across route.go (3,314 lines). Counts: PORTED 4, PARTIAL 21, MISSING 23, NOT_APPLICABLE 9, DEFERRED 0 auto