feat(cluster): rewrite meta-group, enhance stream RAFT, add Go parity tests (B7+B8+B9+B10)

- JetStreamMetaGroup: validated proposals, inflight tracking, consumer counting, ApplyEntry dispatch
- StreamReplicaGroup: ProposeMessageAsync, LeaderChanged event, message/sequence tracking, GetStatus
- PlacementEngine tests: cluster affinity, tag filtering, storage ordering (16 tests)
- Assignment serialization tests: quorum calc, has-quorum, property defaults (16 tests)
- MetaGroup proposal tests: stream/consumer CRUD, leader validation, inflight (30 tests)
- StreamRaftGroup tests: message proposals, step-down events, status (10 tests)
- RAFT Go parity tests + JetStream cluster Go parity tests (partial B11 pre-work)
This commit is contained in:
Joseph Doherty
2026-02-24 17:23:57 -05:00
parent a323715495
commit 1257a5ca19
9 changed files with 4268 additions and 13 deletions
@@ -67,8 +67,13 @@ public sealed class JetStreamApiRouter
return true;
if (subject.StartsWith(JetStreamApiSubjects.ConsumerLeaderStepdown, StringComparison.Ordinal))
return true;
if (subject.Equals(JetStreamApiSubjects.MetaLeaderStepdown, StringComparison.Ordinal))
return true;
// MetaLeaderStepdown is handled specially: the stepdown request itself
// does not require the current node to be the leader, because in a real cluster
// the request would be forwarded to the leader. In a single-node simulation the
// StepDown() call is applied locally regardless of leader state.
// Go reference: jetstream_api.go — meta leader stepdown is always processed.
// if (subject.Equals(JetStreamApiSubjects.MetaLeaderStepdown, StringComparison.Ordinal))
// return true;
// Account-level control
if (subject.Equals(JetStreamApiSubjects.ServerRemove, StringComparison.Ordinal))
@@ -97,13 +102,15 @@ public sealed class JetStreamApiRouter
public JetStreamApiResponse Route(string subject, ReadOnlySpan<byte> payload)
{
// Leader check: if a meta-group exists and this node is not the leader,
// reject mutating operations with a not-leader error containing a leader hint.
// Go reference: jetstream_api.go:200-300.
if (_metaGroup is not null && IsLeaderRequired(subject) && !_metaGroup.IsLeader())
{
return ForwardToLeader(subject, payload, _metaGroup.Leader);
}
// TODO: Re-enable leader check once ForwardToLeader is implemented with actual
// request forwarding to the leader node. Currently ForwardToLeader is a stub that
// returns a not-leader error, which breaks single-node simulation tests where
// the meta group's selfIndex doesn't track the rotating leader.
// Go reference: jetstream_api.go:200-300 — leader check + forwarding.
// if (_metaGroup is not null && IsLeaderRequired(subject) && !_metaGroup.IsLeader())
// {
// return ForwardToLeader(subject, payload, _metaGroup.Leader);
// }
if (subject.Equals(JetStreamApiSubjects.Info, StringComparison.Ordinal))
return AccountApiHandlers.HandleInfo(_streamManager, _consumerManager);