- Fix pull consumer fetch: send original stream subject in HMSG (not inbox) so NATS client distinguishes data messages from control messages - Fix MaxAge expiry: add background timer in StreamManager for periodic pruning - Fix JetStream wire format: Go-compatible anonymous objects with string enums, proper offset-based pagination for stream/consumer list APIs - Add 42 E2E black-box tests (core messaging, auth, TLS, accounts, JetStream) - Add ~1000 parity tests across all subsystems (gaps closure) - Update gap inventory docs to reflect implementation status
31 KiB
31 KiB
Gateways — Gap Analysis
This file tracks what has and hasn't been ported from Go to .NET for the Gateways 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:
- Extract all exported types (structs, interfaces, type aliases)
- Extract all exported methods on those types (receiver functions)
- Extract all exported standalone functions
- Note key constants, enums, and protocol states
- Note important unexported helpers that implement core logic (functions >20 lines)
- 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:
- Search for a matching type, method, or function in .NET
- If found, compare the behavior: does it handle the same edge cases? Same error paths?
- If partially implemented, note what's missing
- If not found, note it as MISSING
Step 3: Cross-Reference Tests
Compare Go test functions against .NET test methods:
- For each Go
Test*function, check if a corresponding .NET[Fact]or[Theory]exists - Note which test scenarios are covered and which are missing
- 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 Gateways
- Gateways connect separate NATS clusters. They start in "optimistic" mode (forward everything) then switch to "interest-only" mode.
- Reply subject mapping:
_GR_.<cluster>.<hash>.<reply>prevents routing loops. - Gateway connections use
ClientKind = GATEWAY.
Go Reference Files (Source)
golang/nats-server/server/gateway.go— Inter-cluster bridges (~3,400 lines). Interest-only mode optimizes traffic. Reply subject mapping (_GR_.prefix) avoids cross-cluster conflicts.
Go Reference Files (Tests)
golang/nats-server/server/gateway_test.gogolang/nats-server/test/gateway_test.go(integration)
.NET Implementation Files (Source)
src/NATS.Server/Gateways/(all files)
.NET Implementation Files (Tests)
tests/NATS.Server.Tests/Gateways/
Gap Inventory
golang/nats-server/server/gateway.go
Constants, Variables, and Enums
| Go Symbol | Go File:Line | Status | .NET Equivalent | Notes |
|---|---|---|---|---|
SetGatewaysSolicitDelay |
gateway.go:76 | NOT_APPLICABLE | — | Test-only helper; sets atomic delay before soliciting gateways. Go-specific test hook. |
ResetGatewaysSolicitDelay |
gateway.go:83 | NOT_APPLICABLE | — | Test-only helper; resets atomic delay. Go-specific test hook. |
GatewayDoNotForceInterestOnlyMode |
gateway.go:130 | NOT_APPLICABLE | — | Test-only global flag; disables forced interest-only mode in tests. Go-specific test hook. |
GatewayInterestMode (enum: Optimistic/Transitioning/InterestOnly) |
gateway.go:94–111 | PORTED | src/NATS.Server/Gateways/GatewayInterestTracker.cs:16 |
GatewayInterestMode enum with identical three values. |
GatewayInterestMode.String() |
gateway.go:113 | NOT_APPLICABLE | — | Go stringer pattern; C# uses ToString() automatically. |
gwReplyPrefix / gwReplyPrefixLen / gwHashLen / offset constants |
gateway.go:49–58 | PORTED | src/NATS.Server/Gateways/ReplyMapper.cs:12–17 |
Reply-prefix/length/hash-length constants are explicitly defined (GatewayReplyPrefix, GatewayReplyPrefixLen, GatewayHashLen, plus legacy counterparts). |
oldGWReplyPrefix / oldGWReplyPrefixLen / oldGWReplyStart |
gateway.go:43–46 | PORTED | src/NATS.Server/Gateways/ReplyMapper.cs:13,15,31 |
Legacy $GR. prefix constants and old-prefix detection are implemented via IsGatewayRoutedSubject(..., out isOldPrefix). |
gatewayTLSInsecureWarning |
gateway.go:71 | PORTED | src/NATS.Server/Gateways/GatewayManager.cs:68 |
Gateway TLS insecure warning constant is defined for parity/documentation and diagnostic use. |
Structs / Types
| Go Symbol | Go File:Line | Status | .NET Equivalent | Notes |
|---|---|---|---|---|
srvGateway struct |
gateway.go:134 | PARTIAL | src/NATS.Server/Gateways/GatewayManager.cs:66 |
GatewayManager covers outbound/inbound maps, accept loop, discovery, registration, and stats. Missing: RTT-ordered outbound list (outo), totalQSubs atomic counter, pasi per-account subscription interest map, rsubs recent-subscription sync.Map, sIDHash/routesIDByHash for reply routing, sqbsz/recSubExp, and oldHash/oldReplyPfx for backward compat. |
sitally struct |
gateway.go:189 | MISSING | — | Subject-interest tally (ref count + queue flag) used in pasi.m. No equivalent in .NET. |
gatewayCfg struct |
gateway.go:194 | PARTIAL | src/NATS.Server/Configuration/GatewayOptions.cs:27 (RemoteGatewayOptions) |
Added parity state and helpers for hash/oldHash, implicit, connection attempts, TLS host capture, URL add/update/get flows, and varz URL-update flag. Remaining gap: per-remote TLS config wiring into active gateway dial/handshake path. |
gateway struct (per-client) |
gateway.go:207 | MISSING | — | Per-connection gateway state (outbound flag, outsim sync.Map, insim map, connectURL, useOldPrefix, interestOnlyMode, remoteName). Absorbed into GatewayConnection but without full per-message interest tracking. |
outsie struct |
gateway.go:229 | PARTIAL | src/NATS.Server/Gateways/GatewayInterestTracker.cs |
GatewayInterestTracker.AccountState covers mode and no-interest set. Missing: per-account sl *Sublist for queue-sub tracking in InterestOnly mode, qsubs counter. |
insie struct |
gateway.go:256 | MISSING | — | Inbound per-account no-interest set with mode tracking. No distinct type in .NET; GatewayInterestTracker handles outbound-side equivalent but not the inbound RS-/RS+ tracking map. |
gwReplyMap struct |
gateway.go:261 | PARTIAL | src/NATS.Server/Gateways/ReplyMapper.cs:289 (CacheEntry) |
ReplyMapCache.CacheEntry provides key/value/TTL. Missing: Go stores ms string (routed subject) and exp int64 (Unix nano expiry); .NET uses DateTime but does not store the destination server/cluster hash separately. |
gwReplyMapping struct |
gateway.go:266 | PARTIAL | src/NATS.Server/Gateways/ReplyMapper.cs:181 (ReplyMapCache) |
ReplyMapCache provides LRU+TTL. Missing: atomic check int32 field for fast-path bypass; Go mapping is per-client or per-account with explicit locker, .NET is a single shared cache. |
GatewayConnectionState enum |
gateway.go (n/a) | PORTED | src/NATS.Server/Gateways/GatewayManager.cs:14 |
.NET addition: lifecycle states (Connecting/Connected/Disconnected/Draining). No direct Go equivalent but covers the connection state machine. |
GatewayRegistration |
gateway.go (n/a) | PORTED | src/NATS.Server/Gateways/GatewayManager.cs:26 |
.NET-specific registration record with stats counters. |
GatewayReconnectPolicy |
gateway.go:689 | PORTED | src/NATS.Server/Gateways/GatewayManager.cs:43 |
Exponential backoff with jitter matches Go's reconnect delay logic. |
GatewayInfo |
gateway.go (Info struct) | PARTIAL | src/NATS.Server/Gateways/GatewayInfo.cs:7 |
Covers name and URLs for gossip discovery. Go's Info struct has many more gateway-related fields (GatewayCmd, GatewayCmdPayload, GatewayNRP, GatewayIOM, GatewayURLs, etc.) that are not in this type. |
Functions and Methods — srvGateway receiver
| Go Symbol | Go File:Line | Status | .NET Equivalent | Notes |
|---|---|---|---|---|
srvGateway.updateRemotesTLSConfig |
gateway.go:426 | MISSING | — | Config-reload TLS update for remote gateway connections. TLS not implemented in .NET. |
srvGateway.rejectUnknown |
gateway.go:476 | PARTIAL | src/NATS.Server/Configuration/GatewayOptions.cs:11 |
GatewayOptions.RejectUnknown property exists but is not enforced in the accept-loop or handshake logic. |
srvGateway.generateInfoJSON |
gateway.go:649 | MISSING | — | Generates the gateway INFO protocol JSON. No INFO protocol generation in .NET. |
srvGateway.getClusterHash |
gateway.go:2892 | MISSING | — | Returns 6-byte cluster hash for reply routing. No cluster hash computed at runtime in .NET. |
srvGateway.shouldMapReplyForGatewaySend |
gateway.go:2507 | MISSING | — | Checks recent subscriptions (rsubs) to decide whether to map a reply subject. rsubs not ported. |
srvGateway.orderOutboundConnectionsLocked |
gateway.go:1764 | MISSING | — | Sorts outbound connections by lowest RTT. RTT-based ordering not implemented. |
srvGateway.orderOutboundConnections |
gateway.go:1771 | MISSING | — | Locked wrapper for orderOutboundConnectionsLocked. |
Functions and Methods — Server receiver
| Go Symbol | Go File:Line | Status | .NET Equivalent | Notes |
|---|---|---|---|---|
validateGatewayOptions |
gateway.go:306 | PORTED | src/NATS.Server/Gateways/GatewayManager.cs:113 (ValidateGatewayOptions) |
Basic gateway config validation implemented for required name, valid port range, and non-empty remotes. |
getGWHash (standalone) |
gateway.go:335 | PORTED | src/NATS.Server/Gateways/ReplyMapper.cs:75 (ComputeGatewayHash) |
Deterministic short gateway hash helper implemented (6-char hex). |
getOldHash (standalone) |
gateway.go:339 | PORTED | src/NATS.Server/Gateways/ReplyMapper.cs:85 (ComputeOldGatewayHash) |
Deterministic legacy short hash helper implemented (4-char hex). |
Server.newGateway |
gateway.go:350 | PARTIAL | src/NATS.Server/Gateways/GatewayManager.cs:90 |
GatewayManager constructor covers basic setup. Missing: hash computation, reply prefix assembly, oldReplyPfx, pasi init, resolver config. |
Server.startGateways |
gateway.go:487 | PARTIAL | src/NATS.Server/Gateways/GatewayManager.cs:161 (StartAsync) |
StartAsync starts accept loop and connects to remotes. Missing: solicit delay, cluster-formation wait. |
Server.startGatewayAcceptLoop |
gateway.go:511 | PARTIAL | src/NATS.Server/Gateways/GatewayManager.cs:326 (AcceptLoopAsync) |
Accept loop exists. Missing: TLS config, authRequired, reject-unknown check, advertising, GatewayIOM flag, INFO protocol send. |
Server.setGatewayInfoHostPort |
gateway.go:595 | MISSING | — | Configures gateway listener host/port with advertise override and non-local IP resolution. |
Server.solicitGateways |
gateway.go:668 | PARTIAL | src/NATS.Server/Gateways/GatewayManager.cs:173 (StartAsync remote loop) |
Connects to configured remotes. Missing: implicit vs explicit distinction, goroutine-per-remote with proper lifecycle. |
Server.reconnectGateway |
gateway.go:689 | PORTED | src/NATS.Server/Gateways/GatewayManager.cs:149 (ReconnectGatewayAsync) |
Exponential backoff reconnect delay with jitter. |
Server.solicitGateway |
gateway.go:706 | PARTIAL | src/NATS.Server/Gateways/GatewayManager.cs:358 (ConnectWithRetryAsync) |
Retry loop with multiple URL support. Missing: random URL selection, shouldReportConnectErr throttling, implicit gateway retry limits, DNS resolution via resolver, ConnectBackoff flag. |
srvGateway.hasInbound |
gateway.go:790 | PORTED | src/NATS.Server/Gateways/GatewayManager.cs:345 (HasInbound) |
Inbound-connection presence check is implemented by remote server id. |
Server.createGateway |
gateway.go:805 | PARTIAL | src/NATS.Server/Gateways/GatewayManager.cs:344 (HandleInboundAsync) and ConnectWithRetryAsync |
Creates client connection for inbound/outbound. Missing: full client lifecycle, TLS handshake, CONNECT/INFO protocol, expectConnect flag, ping timer setup, temp-client registration. |
client.sendGatewayConnect |
gateway.go:958 | PARTIAL | src/NATS.Server/Gateways/GatewayConnection.cs:113 (PerformOutboundHandshakeAsync) |
Handshake sends server ID. Go sends full CONNECT JSON with auth, TLS flag, gateway name. .NET sends a simplified GATEWAY {serverId} line. |
client.processGatewayConnect |
gateway.go:993 | MISSING | — | Parses CONNECT from inbound gateway; validates gateway field, rejects wrong port/unknown gateways. No protocol parsing in .NET. |
client.processGatewayInfo |
gateway.go:1045 | MISSING | — | Handles INFO protocol for both outbound (first connect + gossip) and inbound (register, switch to interest-only, send queue subs). Core of gateway handshake. Not ported. |
Server.gossipGatewaysToInboundGateway |
gateway.go:1253 | MISSING | — | Sends INFO gossip for all known gateways to a new inbound connection for full-mesh formation. |
Server.forwardNewGatewayToLocalCluster |
gateway.go:1279 | MISSING | — | Floods cluster routes with new gateway INFO to ensure all nodes connect. |
Server.sendQueueSubsToGateway |
gateway.go:1311 | PARTIAL | src/NATS.Server/Gateways/GatewayConnection.cs:72 (AddQueueSubscription) |
Registers queue subs locally. Missing: actual RS+ wire protocol emission to the remote peer. |
Server.sendAccountSubsToGateway |
gateway.go:1319 | PARTIAL | src/NATS.Server/Gateways/GatewayManager.cs:210 (SendAccountSubscriptions) |
Sends subject list to named gateway. Missing: wire protocol emission (RS+ with account prefix) and mode switch to InterestOnly. |
gwBuildSubProto (standalone) |
gateway.go:1323 | MISSING | — | Builds RS+/RS- binary protocol buffer for a given account/subject map. |
Server.sendSubsToGateway |
gateway.go:1342 | MISSING | — | Full subscription send loop (queue subs on connect, or all subs for an account on mode switch). |
Server.processGatewayInfoFromRoute |
gateway.go:1394 | MISSING | — | Handles gateway gossip INFO received via a cluster route connection. |
Server.sendGatewayConfigsToRoute |
gateway.go:1406 | MISSING | — | Sends known outbound gateway configs to a new route connection. |
Server.processImplicitGateway |
gateway.go:1453 | PARTIAL | src/NATS.Server/Gateways/GatewayManager.cs:119 (ProcessImplicitGateway) |
Records discovered gateway name. Missing: URL augmentation of existing config, creation of gatewayCfg, launching solicitGateway goroutine for the new implicit remote. |
Server.NumOutboundGateways |
gateway.go:1501 | PORTED | src/NATS.Server/NatsServer.cs:155 (NumOutboundGateways) |
Public server-facing outbound gateway count now exposed. |
Server.numOutboundGateways |
gateway.go:1506 | PORTED | src/NATS.Server/Gateways/GatewayManager.cs:331 (NumOutboundGateways) |
Manager now computes outbound count from live connection direction (IsOutbound). |
Server.numInboundGateways |
gateway.go:1514 | PORTED | src/NATS.Server/Gateways/GatewayManager.cs:338 (NumInboundGateways), src/NATS.Server/NatsServer.cs:156 |
Inbound gateway count is now tracked and exposed. |
Server.getRemoteGateway |
gateway.go:1522 | PARTIAL | src/NATS.Server/Gateways/GatewayManager.cs:264 (GetRegistration) |
Returns registration by name. Missing: returns gatewayCfg in Go (with TLS, URLs, hash); .NET returns GatewayRegistration (only state/stats). |
gatewayCfg.bumpConnAttempts |
gateway.go:1530 | PORTED | src/NATS.Server/Configuration/GatewayOptions.cs:57 (RemoteGatewayOptions.BumpConnAttempts) |
Added connection-attempt increment helper. |
gatewayCfg.getConnAttempts |
gateway.go:1537 | PORTED | src/NATS.Server/Configuration/GatewayOptions.cs:59 (RemoteGatewayOptions.GetConnAttempts) |
Added connection-attempt read helper. |
gatewayCfg.resetConnAttempts |
gateway.go:1545 | PORTED | src/NATS.Server/Configuration/GatewayOptions.cs:61 (RemoteGatewayOptions.ResetConnAttempts) |
Added connection-attempt reset helper. |
gatewayCfg.isImplicit |
gateway.go:1552 | PORTED | src/NATS.Server/Configuration/GatewayOptions.cs:63 (RemoteGatewayOptions.IsImplicit) |
Added implicit-vs-explicit gateway config query. |
gatewayCfg.getURLs |
gateway.go:1561 | PORTED | src/NATS.Server/Configuration/GatewayOptions.cs:65 (RemoteGatewayOptions.GetUrls) |
Added normalized + shuffled URL list helper for connection attempts. |
gatewayCfg.getURLsAsStrings |
gateway.go:1576 | PORTED | src/NATS.Server/Configuration/GatewayOptions.cs:84 (RemoteGatewayOptions.GetUrlsAsStrings) |
Added URL string projection helper for gossip/config sync paths. |
gatewayCfg.updateURLs |
gateway.go:1588 | PORTED | src/NATS.Server/Configuration/GatewayOptions.cs:92 (RemoteGatewayOptions.UpdateUrls) |
Added merged configured+discovered URL rebuild helper with normalization/deduplication. |
gatewayCfg.saveTLSHostname |
gateway.go:1612 | PORTED | src/NATS.Server/Configuration/GatewayOptions.cs:100 (RemoteGatewayOptions.SaveTlsHostname) |
Added TLS hostname extraction/storage from URL host. |
gatewayCfg.addURLs |
gateway.go:1621 | PORTED | src/NATS.Server/Configuration/GatewayOptions.cs:106 (RemoteGatewayOptions.AddUrls) |
Added incremental discovered-URL add helper with normalization/deduplication. |
Server.addGatewayURL |
gateway.go:1648 | MISSING | — | Adds a URL to the server's gateway URL set and regenerates INFO JSON. |
Server.removeGatewayURL |
gateway.go:1661 | MISSING | — | Removes a URL from the gateway URL set and regenerates INFO JSON. |
Server.sendAsyncGatewayInfo |
gateway.go:1676 | MISSING | — | Sends updated INFO to all inbound gateway connections (e.g., after URL change). |
Server.getGatewayURL |
gateway.go:1688 | PORTED | src/NATS.Server/NatsServer.cs:259 |
Added gateway listen URL accessor that returns configured listen endpoint when gateway manager is present. |
Server.getGatewayName |
gateway.go:1697 | PORTED | src/NATS.Server/NatsServer.cs:260 |
Added gateway name accessor returning configured gateway cluster name. |
Server.getAllGatewayConnections |
gateway.go:1703 | MISSING | — | Collects all inbound + outbound gateway clients into a map. |
Server.registerInboundGatewayConnection |
gateway.go:1720 | PARTIAL | src/NATS.Server/Gateways/GatewayManager.cs:392 (Register) |
Register adds connection to dictionary. Missing: separate inbound map keyed by CID. |
Server.registerOutboundGatewayConnection |
gateway.go:1728 | PARTIAL | src/NATS.Server/Gateways/GatewayManager.cs:392 (Register) |
Register handles registration. Missing: duplicate-prevention logic (return false if name already exists), RTT-ordered outo list. |
Server.getOutboundGatewayConnection |
gateway.go:1743 | PORTED | src/NATS.Server/Gateways/GatewayManager.cs:352 (GetOutboundGatewayConnection) |
Outbound connection lookup by remote server id is implemented. |
Server.getOutboundGatewayConnections |
gateway.go:1752 | PORTED | src/NATS.Server/Gateways/GatewayManager.cs:359 (GetOutboundGatewayConnections) |
Outbound connection snapshot enumeration is implemented. |
Server.getInboundGatewayConnections |
gateway.go:1778 | PORTED | src/NATS.Server/Gateways/GatewayManager.cs:366 (GetInboundGatewayConnections) |
Inbound connection snapshot enumeration is implemented. |
Server.removeRemoteGatewayConnection |
gateway.go:1788 | PARTIAL | src/NATS.Server/Gateways/GatewayManager.cs:416 (WatchConnectionAsync) |
Removes connection and decrements stats. Missing: outbound-specific cleanup (delete from outo/out, remove qsub tracking from totalQSubs), inbound-specific cleanup (remove _R_ subscriptions). |
Server.GatewayAddr |
gateway.go:1862 | PORTED | src/NATS.Server/NatsServer.cs:258 |
Added gateway address accessor. .NET returns host:port string endpoint rather than Go *net.TCPAddr. |
client.processGatewayAccountUnsub |
gateway.go:1875 | PARTIAL | src/NATS.Server/Gateways/GatewayInterestTracker.cs:86 (TrackNoInterest) |
Tracks no-interest at account level. Missing: handling of queue subs (reset ni map but keep entry if qsubs > 0), Go's nil-vs-entry distinction in outsim. |
client.processGatewayAccountSub |
gateway.go:1904 | PARTIAL | src/NATS.Server/Gateways/GatewayInterestTracker.cs:61 (TrackInterest) |
Clears no-interest in optimistic mode. Missing: queue-sub check (don't delete entry if qsubs > 0). |
client.processGatewayRUnsub |
gateway.go:1934 | MISSING | — | Parses RS- protocol; for optimistic mode stores in ni map, for InterestOnly/queue removes from sublist. Full RS- processing not ported. |
client.processGatewayRSub |
gateway.go:2029 | MISSING | — | Parses RS+ protocol; registers interest in sublist for queue subs and InterestOnly mode. Full RS+ processing not ported. |
client.gatewayInterest |
gateway.go:2165 | PARTIAL | src/NATS.Server/Gateways/GatewayInterestTracker.cs:113 (ShouldForward) |
ShouldForward handles Optimistic/Transitioning/InterestOnly modes. Missing: separate queue-sub result (*SublistResult) return, interestOnlyMode flag override, emptyResult guard. |
Server.switchAccountToInterestMode |
gateway.go:2211 | PORTED | src/NATS.Server/Gateways/GatewayInterestTracker.cs:144 (SwitchToInterestOnly) |
Switches account to InterestOnly. Go iterates all inbound connections; .NET operates per-tracker instance. |
Server.maybeSendSubOrUnsubToGateways |
gateway.go:2237 | PARTIAL | src/NATS.Server/Gateways/GatewayManager.cs:193 (PropagateLocalSubscription/Unsubscription) |
Propagates sub/unsub to inbound gateways. Missing: per-mode decision (optimistic ni-map check vs InterestOnly always-send), wildcard cleanup of ni-map, A+ send when clearing A-, actual wire protocol. |
Server.sendQueueSubOrUnsubToGateways |
gateway.go:2335 | PARTIAL | src/NATS.Server/Gateways/GatewayManager.cs:199 (PropagateLocalUnsubscription) |
Propagates queue sub changes. Missing: wire RS+/RS- protocol, A- clearing logic. |
Server.gatewayUpdateSubInterest |
gateway.go:2391 | MISSING | — | Ref-counted pasi map update + recent-sub tracking + triggers send to gateways. Core subscription-interest accounting not ported. |
isGWRoutedReply (standalone) |
gateway.go:2484 | PORTED | src/NATS.Server/Gateways/ReplyMapper.cs:17 (HasGatewayReplyPrefix) |
Detects _GR_. prefix with length guard. |
isGWRoutedSubjectAndIsOldPrefix (standalone) |
gateway.go:2490 | PORTED | src/NATS.Server/Gateways/ReplyMapper.cs:31 (IsGatewayRoutedSubject) |
Implemented explicit routed-subject detection with old-prefix flag output (isOldPrefix) for _GR_. and $GR.. |
hasGWRoutedReplyPrefix (standalone) |
gateway.go:2502 | PORTED | src/NATS.Server/Gateways/ReplyMapper.cs:17 (HasGatewayReplyPrefix) |
Equivalent prefix check. |
srvGateway.shouldMapReplyForGatewaySend |
gateway.go:2507 | MISSING | — | Checks rsubs sync.Map to decide if a reply subject needs gateway mapping. No rsubs equivalent. |
client.sendMsgToGateways |
gateway.go:2540 | PARTIAL | src/NATS.Server/Gateways/GatewayManager.cs:181 (ForwardMessageAsync) |
Iterates connections and sends. Missing: direct-send path for _GR_ subjects (hash routing), queue group filtering, reply subject mapping, header stripping for non-header peers, message tracing, per-account stats, RTT-ordered iteration. |
Server.gatewayHandleAccountNoInterest |
gateway.go:2787 | PARTIAL | Partially in GatewayInterestTracker.TrackNoInterest |
Sends A- under pasi lock. Missing: pasi lock coordination, actual A- wire protocol emission from inbound side. |
client.sendAccountUnsubToGateway |
gateway.go:2802 | PARTIAL | src/NATS.Server/Gateways/GatewayConnection.cs:142 (SendAMinusAsync) |
Sends A- wire protocol. Missing: idempotency guard (don't send if already sent, tracked via nil entry in insim). |
Server.gatewayHandleSubjectNoInterest |
gateway.go:2830 | MISSING | — | On missing subject interest: sends RS- under pasi lock, counts RS-, triggers mode switch at threshold. Core interest-only negotiation not ported. |
Server.storeRouteByHash |
gateway.go:2901 | MISSING | — | Stores route client keyed by server-ID hash for gateway reply routing. |
Server.removeRouteByHash |
gateway.go:2909 | MISSING | — | Removes route entry by hash. |
Server.getRouteByHash |
gateway.go:2918 | MISSING | — | Looks up route by hash for per-account or pool routing. Used in handleGatewayReply. |
getSubjectFromGWRoutedReply (standalone) |
gateway.go:2948 | PORTED | src/NATS.Server/Gateways/ReplyMapper.cs:74 (TryRestoreGatewayReply) |
Extracts original subject from routed reply; handles both old and new prefix. |
client.handleGatewayReply |
gateway.go:2963 | MISSING | — | On inbound message: detects _GR_ prefix, decodes cluster/server hash, routes to origin route or delivers locally. Core gateway reply routing not ported. |
client.processInboundGatewayMsg |
gateway.go:3107 | MISSING | — | Main inbound message processor: handles GW reply, account lookup, no-interest signaling, message delivery. Not ported. |
client.gatewayAllSubsReceiveStart |
gateway.go:3183 | PARTIAL | src/NATS.Server/Gateways/GatewayInterestTracker.cs:86 (mode=Transitioning path) |
Go sets mode to Transitioning; .NET sets Transitioning mode when threshold crossed. Missing: command-protocol parsing, explicit start triggered by INFO command. |
client.gatewayAllSubsReceiveComplete |
gateway.go:3216 | PARTIAL | src/NATS.Server/Gateways/GatewayInterestTracker.cs:144 (SwitchToInterestOnly) |
Final switch to InterestOnly. Go clears ni map and sets mode; .NET does equivalent. Missing: triggered by INFO command with gatewayCmdAllSubsComplete. |
getAccountFromGatewayCommand (standalone) |
gateway.go:3240 | MISSING | — | Extracts account from GatewayCmdPayload in INFO protocol. Not ported. |
client.gatewaySwitchAccountToSendAllSubs |
gateway.go:3260 | PARTIAL | src/NATS.Server/Gateways/GatewayInterestTracker.cs:144 (SwitchToInterestOnly) |
Switches mode and triggers async all-subs send. Missing: INFO command emission (gatewayCmdAllSubsStart/gatewayCmdAllSubsComplete), async sendAccountSubsToGateway goroutine. |
Server.trackGWReply |
gateway.go:3324 | PARTIAL | src/NATS.Server/Gateways/ReplyMapper.cs:231 (ReplyMapCache.Set) |
Caches reply mapping with TTL. Missing: per-client vs per-account duality, gwrm.m sync.Map for background cleanup, check int32 atomic flag, gwrm.ch channel to trigger expiry timer. |
Server.startGWReplyMapExpiration |
gateway.go:3371 | PARTIAL | src/NATS.Server/Gateways/ReplyMapper.cs:267 (ReplyMapCache.PurgeExpired) |
Purge is manual on-demand. Go runs a dedicated goroutine with timer reset on new entries via channel. No background expiry goroutine in .NET. |
gwReplyMapping.get |
gateway.go:280 | PORTED | src/NATS.Server/Gateways/ReplyMapper.cs:202 (ReplyMapCache.TryGet) |
LRU get with TTL check. |
RemoteGatewayOpts.clone |
gateway.go:290 | PORTED | src/NATS.Server/Configuration/GatewayOptions.cs:36 (RemoteGatewayOptions.Clone) |
Deep-copy helper implemented for remote gateway option name + URL list. |
Additional .NET-Only Types (No Go Equivalent)
| .NET Symbol | .NET File:Line | Notes |
|---|---|---|
GatewayCommandType enum |
src/NATS.Server/Gateways/GatewayCommands.cs:9 |
.NET-specific wire command enum; Go uses string constants and byte dispatch. |
GatewayCommands static class |
src/NATS.Server/Gateways/GatewayCommands.cs:31 |
Wire-protocol constants and formatters. Partially maps to Go's rSubBytes/rUnsubBytes/aSubBytes/aUnsubBytes/InfoProto but uses a simplified format. |
GatewayMessage record |
src/NATS.Server/Gateways/GatewayConnection.cs:346 |
DTO for received messages; no direct Go equivalent. |
ReplyMapCache |
src/NATS.Server/Gateways/ReplyMapper.cs:181 |
LRU+TTL cache for reply mappings. |
Keeping This File Updated
After porting work is completed:
- Update status: Change
MISSING → PORTEDorPARTIAL → PORTEDfor each item completed - Add .NET path: Fill in the ".NET Equivalent" column with the actual file:line
- Re-count LOC: Update the LOC numbers in
stillmissing.md:# Re-count .NET source LOC for this module find src/NATS.Server/Gateways/ -name '*.cs' -type f -exec cat {} + | wc -l # Re-count .NET test LOC for this module find tests/NATS.Server.Tests/Gateways/ -name '*.cs' -type f -exec cat {} + | wc -l - Add a changelog entry below with date and summary of what was ported
- 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-26 | Executed gateways batch 4 accessor parity slice: added server gateway accessors (GatewayAddr, GetGatewayURL, GetGatewayName) and targeted tests (GatewayServerAccessorParityBatch4Tests). Reclassified 3 rows to PORTED. |
codex |
| 2026-02-25 | File created with LLM analysis instructions | auto |
| 2026-02-25 | Full gap inventory completed: analyzed all ~3,427 lines of gateway.go; classified 80+ Go symbols against 6 .NET source files. Final counts: 9 PORTED, 35 PARTIAL, 37 MISSING, 5 NOT_APPLICABLE. | claude-sonnet-4-6 |
| 2026-02-25 | Ported gateway parity helper batch: reply-prefix/hash constants and old-prefix detection, gateway hash helpers (getGWHash/getOldHash analogs), gateway option validator, TLS warning constant, and RemoteGatewayOptions.Clone; added focused tests and updated status rows. |
codex |
| 2026-02-25 | Ported gateway connection-direction parity batch: added inbound/outbound classification (IsOutbound), count/lookups (NumOutboundGateways, NumInboundGateways, HasInbound, outbound/inbound connection snapshots), and server wrappers with focused tests. |
codex |
| 2026-02-25 | Ported gateway remote-config parity batch: added RemoteGatewayOptions attempt counters and URL lifecycle helpers (Bump/Get/ResetConnAttempts, IsImplicit, GetUrls, GetUrlsAsStrings, UpdateUrls, SaveTlsHostname, AddUrls) with focused tests (GatewayRemoteConfigParityBatch3Tests). |
codex |