feat: add atomic batch publish engine & versioning support (Tasks 9-10)

- AtomicBatchPublishEngine: stage/commit/rollback semantics for batch publish
- JsVersioning: API level negotiation and stream/consumer metadata
- Fix NormalizeConfig missing AllowAtomicPublish, Metadata, PersistMode copy
- 46 batch publish tests + 67 versioning tests, all passing
This commit is contained in:
Joseph Doherty
2026-02-24 22:05:07 -05:00
parent cd009b9342
commit b80316a42f
10 changed files with 953 additions and 5 deletions

View File

@@ -63,6 +63,13 @@ public sealed class StreamManager
if (normalized.SubjectDeleteMarkerTtlMs > 0 && !string.IsNullOrWhiteSpace(normalized.Mirror))
return JetStreamApiResponse.ErrorResponse(10054, "mirror configuration can not have subject delete marker TTL");
// Go: NewJSMirrorWithAtomicPublishError (10198) — mirror + AllowAtomicPublish is invalid.
// Reference: server/stream.go:1735-1737
if (normalized.AllowAtomicPublish && !string.IsNullOrWhiteSpace(normalized.Mirror))
return JetStreamApiResponse.ErrorResponse(
AtomicBatchPublishErrorCodes.MirrorWithAtomicPublish,
"stream mirrors can not also use atomic publishing");
// Go: RePublish cycle detection — destination must not overlap stream subjects.
// Reference: server/stream.go:1060-1080 (checkRePublish)
if (!string.IsNullOrWhiteSpace(normalized.RePublishDest))
@@ -389,6 +396,14 @@ public sealed class StreamManager
SubjectDeleteMarkerTtlMs = config.SubjectDeleteMarkerTtlMs,
// Go: StreamConfig.AllowMsgSchedules
AllowMsgSchedules = config.AllowMsgSchedules,
// Go: StreamConfig.AllowMsgCounter — CRDT counter semantics
AllowMsgCounter = config.AllowMsgCounter,
// Go: StreamConfig.AllowAtomicPublish — atomic batch publish
AllowAtomicPublish = config.AllowAtomicPublish,
// Go: StreamConfig.PersistMode — async vs sync persistence
PersistMode = config.PersistMode,
// Go: StreamConfig.Metadata — user and server key/value metadata
Metadata = config.Metadata == null ? null : new Dictionary<string, string>(config.Metadata),
};
return copy;