Adds GatewayReconnectPolicy with exponential backoff and jitter, and
reconnect attempt tracking (GetReconnectAttempts, ResetReconnectAttempts,
ReconnectGatewayAsync) to GatewayManager. 10 new tests cover delay
calculation, cap behaviour, jitter, and per-gateway counter isolation.
Adds per-account subscription tracking to GatewayConnection via
AddAccountSubscription/RemoveAccountSubscription/GetAccountSubscriptions/
AccountSubscriptionCount, with corresponding SendAccountSubscriptions and
GetAccountSubscriptions helpers on GatewayManager. Covered by 10 new unit tests.
Adds LoggingChangeResult and ApplyLoggingChanges to ConfigReloader, deriving
an effective log level from Debug/Trace flags (Trace > Debug > Information) and
reporting flag-level and level-string changes for hot-reload notification.
Add ApplyJetStreamConfigChanges to ConfigReloader for detecting MaxMemoryStore,
MaxFileStore, and Domain changes during hot reload. Add Domain property to
JetStreamOptions. Port 10 unit tests covering all change permutations.
Adds ClusterConfigChangeResult and ApplyClusterConfigChanges to ConfigReloader,
comparing route/gateway/leaf URL sets between old and new NatsOptions and reporting
added/removed routes for connection reconciliation on hot reload.
Add PropagateAuthChanges to ConfigReloader that compares Users, Accounts,
and Authorization token between old and new NatsOptions, returning an
AuthChangeResult describing which auth fields changed for connection re-evaluation.
Add FlapperState class and per-client exponential backoff tracking to
MqttSessionStore. New TrackConnectDisconnect(string) overload returns
FlapperState with backoff level and expiry; IsFlapper, GetBackoffMs,
ClearFlapperState, and CheckAndClearStableClients give callers full
visibility and cleanup control. Legacy two-arg overload preserved for
backward compatibility. Ten unit tests cover counting, threshold,
exponential growth, 60s cap, window reset, and stable-client sweep.
Add DeliverRetainedOnSubscribe to MqttRetainedStore, which iterates
GetMatchingRetained and fires a callback with (topic, payload, qos,
retain=true) for each match. Add 11 unit tests covering exact-match,
'+' single-level, '#' multi-level, no-match, cross-level rejection,
callback arguments, return count, and empty-store behaviour.
Implements MqttFlowController with per-subscription SemaphoreSlim-based
slot tracking for QoS 1/2 messages. Replaces the previous catch-and-swallow
pattern with an explicit CurrentCount guard on Release. 10 unit tests added.
Adds WillMessage class, SetWill/ClearWill/GetWill methods to MqttSessionStore,
PublishWillMessage that dispatches via OnPublish delegate (or tracks as delayed
when DelayIntervalSeconds > 0), and 10 unit tests covering all will message behaviors.
Require 4 consecutive short reads before shrinking AdaptiveReadBuffer, matching
the Go server's readLoop behaviour and preventing buffer size thrashing.
Extend PermissionLruCache with SetSub/TryGetSub (internal key prefix "S:")
alongside existing PUB API ("P:" prefix, backward-compatible). Add Invalidate()
and Generation property for generation-based cache invalidation. Add
GenerationId/IncrementGeneration to Account for account-level change signalling.
10 new tests in SubPermissionCacheTests cover all paths.
Adds SlowConsumerTracker class for per-ClientKind slow consumer counting
with configurable threshold callbacks, and extends Account with atomic
IncrementSlowConsumers/SlowConsumerCount/ResetSlowConsumerCount members.
Includes 10 unit tests covering concurrency, threshold firing, and reset.
Implements ClientTraceInfo with TraceMsgDelivery recording and per-client
echo suppression; fixes AccountGoParityTests namespace ambiguity caused by
the new NATS.Server.Tests.Subscriptions test namespace.
Implements RouteResultCache with fixed-capacity LRU eviction and atomic
generation-based invalidation (Go ref: client.go routeCache, maxResultCacheSize=8192).
Fixes AccountGoParityTests namespace ambiguity introduced by new test file.
Add GetMirrorInfo/GetSourceInfo methods to MirrorCoordinator and
SourceCoordinator returning structured MirrorInfoResponse/SourceInfoResponse
records (Name, Lag, Active ms, FilterSubject, Error). Wire both into
StreamManager.GetMirrorInfo and GetSourceInfos for the monitoring API path.
Add ValidateConfigUpdate to StreamManager with immutability rules for storage type,
mirror, sources, and retention policy; sealed stream guard; MaxConsumers decrease
prevention; even-replica rejection; and subject overlap detection against peer streams.
Wire the check into CreateOrUpdate for all update paths. 12 new tests in
ConfigUpdateValidationTests.cs cover all rules including the StreamManager integration test.
Enhance StreamSnapshotService with CreateTarSnapshotAsync / RestoreTarSnapshotAsync
methods that produce a Snappy-compressed TAR archive (stream.json + messages/*.json).
Add CreateTarSnapshotWithDeadlineAsync for deadline-bounded snapshots, and a
SnapshotRestoreResult record carrying stats. 10 new unit tests in
JetStream/Snapshots/StreamSnapshotTests.cs exercise the full create/restore
round-trip, compression format, empty-stream edge case, and deadline enforcement.
Adds ProposeWaitingRequest, RegisterClusterPending, RemoveClusterPending,
GetClusterPendingRequests, and ClusterPendingCount to PullConsumerEngine,
backed by a ConcurrentDictionary keyed by reply subject. Includes 10 xUnit
tests covering quorum checks, pending tracking, and concurrent access patterns.
Add ResetToSequence to ConsumerManager that updates NextSequence,
clears AckProcessor state via new ClearAll(), and zeroes PendingBytes.
Add AckProcessor.SetAckFloor() that prunes pending entries below the
new floor. Go reference: consumer.go:4241 processResetReq.
Implements DeliveryInterestTracker (consumer.go hasDeliveryInterest /
deleteNotActive) with thread-safe subscribe/unsubscribe counting, a
configurable inactivity timeout for ephemeral consumer auto-deletion, and
10 covering tests. Also adds SlopwatchSuppress to a pre-existing
Task.Delay in MaxDeliveriesTests.cs that was already testing time-based expiry.
Add FilterSkipTracker using SubjectMatch.MatchLiteral() for NATS token-based
filter matching with sorted-set skip sequence gap tracking. Includes 14 tests
covering exact match, star/gt wildcards, multi-filter, counters, RecordSkip,
NextUnskippedSequence, PurgeBelow, and Reset.
Adds MaxDeliver property, exceeded sequence tracking, DrainExceeded,
DeliveryExceededPolicy enum, and ScheduleRedelivery enforcement to
AckProcessor; 10 new tests in MaxDeliveriesTests.cs.
Heartbeat frames now include Nats-Pending-Messages and Nats-Pending-Bytes
headers populated from the ConsumerHandle. Flow control frames increment
FlowControlPendingCount; AcknowledgeFlowControl() decrements it. IsFlowControlStalled
returns true when pending count reaches MaxFlowControlPending (2).
Go reference: consumer.go:5222 (sendIdleHeartbeat), consumer.go:5495 (sendFlowControl).
Enhances HandlePause to parse pause_until (RFC3339) for time-bounded
pauses and returns current pause state in the response body. Adds
Paused/PauseUntil properties and PauseResponse factory to
JetStreamApiResponse. Covers 10 new parity tests.
Add JetStream advisory subject constants to EventSubjects and a lightweight
AdvisoryPublisher that publishes stream/consumer lifecycle events to
$JS.EVENT.ADVISORY.* subjects without depending on InternalEventSystem
directly (testable via Action delegate injection).
Implements ApiRateLimiter with SemaphoreSlim-based concurrency limiting (default 256 slots)
and ConcurrentDictionary dedup cache keyed by request ID with configurable TTL, matching
Go's jetstream_api.go maxConcurrentRequests semaphore and dedup window. Also adds
ClusteredRequestProcessor for correlating pending RAFT proposals with waiting callers via
TaskCompletionSource, and SlopwatchSuppressAttribute as a marker for intentional timing-based
tests. 12 ApiRateLimiter tests + 13 ClusteredRequestProcessor tests all pass.
Implement HandleClusteredCreateAsync, HandleClusteredUpdateAsync, and
HandleClusteredDeleteAsync on StreamApiHandlers, and HandleClusteredCreateAsync
and HandleClusteredDeleteAsync on ConsumerApiHandlers. These handlers propose
operations to the meta RAFT group (JetStreamMetaGroup) instead of operating on
the local StreamManager/ConsumerManager, matching the Go jsClusteredStreamRequest
and jsClusteredConsumerRequest patterns (jetstream_cluster.go:7620-8265).
Ten tests in ClusteredApiTests.cs verify: stream create proposes to meta group,
duplicate-stream error, not-leader error (code 10003), stream update, stream
delete, not-found-on-delete, consumer create on stream, consumer-on-missing-stream
error, consumer delete, and not-found consumer delete.
Add ILeaderForwarder interface and DefaultLeaderForwarder, update
JetStreamApiRouter with RouteAsync + ForwardedCount so non-leader nodes
can attempt to forward mutating requests to the meta-group leader before
falling back to a NotLeader error response.
Implements AssignmentCodec with JSON serialization for StreamAssignment
and ConsumerAssignment, plus Snappy compression helpers for large payloads.
Adds 12 tests covering round-trips, error handling, compression, and a
golden fixture for format stability.
Add Version property to StreamAssignment and ConsumerAssignment so future-version
entries from newer cluster peers are gracefully skipped rather than applied incorrectly.
ProcessStreamAssignment and ProcessConsumerAssignment now reject entries with
Version > CurrentVersion (1), incrementing SkippedUnsupportedEntries for operator
visibility. Add MetaEntryType.Unknown with a no-op ApplyEntry handler so unrecognised
RAFT entry types never crash the cluster. Version 0 is treated as current for
backward compatibility with pre-versioned assignments.
Implement IsMember, SetPreferred, RemovePeer, AddPeer, CreateRaftGroup
factory, IsUnderReplicated, and IsOverReplicated on RaftGroup. Add 22
RaftGroupLifecycleTests covering all helpers and quorum size calculation.
Extends PlacementEngine.SelectPeerGroup with three new capabilities ported
from jetstream_cluster.go:7212 selectPeerGroup:
- JetStreamUniqueTag enforcement: PlacementPolicy.UniqueTag (e.g. "az")
ensures no two replicas share the same value for a tag with that prefix,
matching Go's uniqueTagPrefix / checkUniqueTag logic.
- MaxAssetsPerPeer HA limit: peers at or above their asset ceiling are
deprioritised (moved to fallback), not hard-excluded, so selection still
succeeds when no preferred peers remain.
- Weighted scoring: candidates sorted by
score = AvailableStorage - (CurrentAssets * assetCostWeight)
(DefaultAssetCostWeight = 1 GiB) replacing the raw-storage sort, with a
custom weight parameter for testing.
10 new tests in TopologyPlacementTests.cs cover all three features and their
edge cases. All 30 PlacementEngine tests continue to pass.
Add ProcessAddPeer/ProcessRemovePeer to JetStreamMetaGroup for
peer-driven stream reassignment. Includes AddKnownPeer/RemoveKnownPeer
tracked in a HashSet, RemovePeerFromStream, RemapStreamAssignment with
replacement-peer selection from the known pool, and DesiredReplicas on
RaftGroup for under-replication detection. Go ref: jetstream_cluster.go:2290-2439.
Extend ApplyEntry in JetStreamMetaGroup with PeerAdd/PeerRemove dispatch
to the existing Task 12 peer management methods. Add StreamMsgOp (Store,
Remove, Purge) and ConsumerOp (Ack, Nak, Deliver, Term, Progress) enums
plus ApplyStreamMsgOp and ApplyConsumerEntry methods to StreamReplicaGroup.
Extend ApplyCommittedEntriesAsync to parse smsg:/centry: command prefixes
and route to the new apply methods. Add MetaEntryType.PeerAdd/PeerRemove
enum values. 35 new tests in EntryApplicationTests.cs, all passing.
Add ReadIndexAsync() to RaftNode that confirms leader quorum by sending
heartbeat RPCs before returning CommitIndex, avoiding log growth for reads.
Add RaftReadIndexTests.cs with 13 tests covering normal path, non-leader
rejection, partitioned leader timeout, single-node fast path, log stability,
and peer LastContact refresh after heartbeat round.
Add RandomizedElectionTimeout() method to RaftNode returning TimeSpan in
[ElectionTimeoutMinMs, ElectionTimeoutMaxMs) using TotalMilliseconds (not
.Milliseconds component) to prevent synchronized elections after partitions.
Make Random injectable for deterministic testing. Fix SendHeartbeatAsync stub
in NatsRaftTransport and test-local transport implementations to satisfy the
IRaftTransport interface added in Gap 8.7.
Add HasQuorum() to RaftNode that counts peers with LastContact within
2 × ElectionTimeoutMaxMs and returns true only when self + current peers
reaches majority. ProposeAsync now throws InvalidOperationException with
"no quorum" when HasQuorum() returns false, preventing a partitioned
leader from diverging the log. Add 14 tests in RaftQuorumCheckTests.cs
covering single-node, 3-node, 5-node, boundary window, and heartbeat
restore scenarios. Update RaftHealthTests.LastContact_updates_on_successful_replication
to avoid triggering the new quorum guard.
- Add RaftTimeoutNowWire: 16-byte wire type [8:term][8:leaderId] with
Encode/Decode roundtrip, matching Go's sendTimeoutNow wire layout
- Add TimeoutNow(group) subject "$NRG.TN.{group}" to RaftSubjects
- Add SendTimeoutNowAsync to IRaftTransport; implement in both
InMemoryRaftTransport (synchronous delivery) and NatsRaftTransport
(publishes to $NRG.TN.{group})
- Add TransferLeadershipAsync(targetId, ct) to RaftNode: leader sends
TimeoutNow RPC, blocks proposals via _transferInProgress flag, polls
until target becomes leader or 2x election timeout elapses
- Add ReceiveTimeoutNow(term) to RaftNode: target immediately starts
election bypassing pre-vote, updates term if sender's term is higher
- Block ProposeAsync with InvalidOperationException during transfer
- 15 tests in RaftLeadershipTransferTests covering wire roundtrip,
ReceiveTimeoutNow behaviour, proposal blocking, target leadership,
timeout on unreachable peer, and transfer flag lifecycle
- Add SnapshotChunkEnumerator: IEnumerable<byte[]> that splits snapshot data
into fixed-size chunks (default 65536 bytes) and computes CRC32 over the
full payload for integrity validation during streaming transfer
- Add RaftInstallSnapshotChunkWire: 24-byte header + variable data wire type
encoding [snapshotIndex:8][snapshotTerm:4][chunkIndex:4][totalChunks:4][crc32:4][data:N]
- Extend InstallSnapshotFromChunksAsync with optional expectedCrc32 parameter;
validates assembled data against CRC32 before applying snapshot state, throwing
InvalidDataException on mismatch to prevent corrupt state installation
- Fix stub IRaftTransport implementations in test files missing SendTimeoutNowAsync
- Fix incorrect role assertion in RaftLeadershipTransferTests (single-node quorum = 1)
- 15 new tests in RaftSnapshotStreamingTests covering enumeration, reassembly,
CRC correctness, validation success/failure, and wire format roundtrip
Add NumFiltered with generation-based result caching, block-range binary
search helpers (FindFirstBlockAtOrAfter, FindBlockForSequence), and
CheckSkipFirstBlock optimization. FilteredState uses the block index to
skip sealed blocks below the requested start sequence. LoadMsg gains an
O(log n) block fallback for cases where _messages does not hold the
sequence. Generation counter incremented on every write/delete/purge to
invalidate NumFiltered cache entries.
Add 18 tests in FileStoreFilterQueryTests covering literal and wildcard
FilteredState, start-sequence filtering across multiple blocks, NumFiltered
basic counts and cache invalidation, LoadMsg block search, and
CheckSkipFirstBlock behaviour.
Also fix pre-existing slopwatch violations in WriteCacheTests.cs: replace
Task.Delay(150)/Task.Delay(5) with TrackWriteAt using explicit past
timestamps, and restructure Dispose to avoid empty catch blocks.
Reference: golang/nats-server/server/filestore.go:3191 (FilteredState),
golang/nats-server/server/filestore.go:8308 (LoadMsg).