// Copyright 2025 The NATS Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // Adapted from server/jetstream_batching.go in the NATS server Go source. namespace ZB.MOM.NatsNet.Server; // --------------------------------------------------------------------------- // Batching types // --------------------------------------------------------------------------- /// /// Tracks in-progress atomic publish batch groups for a stream. /// Mirrors the batching struct in server/jetstream_batching.go. /// internal sealed class Batching { private readonly Lock _mu = new(); private readonly Dictionary _group = new(StringComparer.Ordinal); public Lock Mu => _mu; public Dictionary Group => _group; } /// /// A single in-progress atomic batch: its temporary store and cleanup timer. /// Mirrors batchGroup in server/jetstream_batching.go. /// internal sealed class BatchGroup { /// Last proposed stream sequence for this batch. public ulong LastSeq { get; set; } /// Temporary backing store for the batch's messages. public object? Store { get; set; } // IStreamStore — session 20 /// Timer that abandons the batch after the configured timeout. public Timer? BatchTimer { get; set; } /// /// Stops the cleanup timer and flushes pending writes so the batch is /// ready to be committed. /// Mirrors batchGroup.readyForCommit. /// public bool ReadyForCommit() { // Stub — full implementation requires IStreamStore.FlushAllPending (session 20). return BatchTimer?.Change(Timeout.Infinite, Timeout.Infinite) != null; } } /// /// Stages consistency-check data for a single atomic batch before it is committed. /// Mirrors batchStagedDiff in server/jetstream_batching.go. /// internal sealed class BatchStagedDiff { /// Message IDs seen in this batch, for duplicate detection. public Dictionary? MsgIds { get; set; } /// Running counter totals, keyed by subject. public Dictionary? Counter { get; set; } // map[string]*msgCounterRunningTotal /// Inflight subject byte/op totals for DiscardNew checks. public Dictionary? Inflight { get; set; } // map[string]*inflightSubjectRunningTotal /// Expected-last-seq-per-subject checks staged in this batch. public Dictionary? ExpectedPerSubject { get; set; } } /// /// Cached expected-last-sequence-per-subject result for a single subject within a batch. /// Mirrors batchExpectedPerSubject in server/jetstream_batching.go. /// internal sealed class BatchExpectedPerSubject { /// Stream sequence of the last message on this subject at proposal time. public ulong SSeq { get; set; } /// Clustered proposal sequence at which this check was computed. public ulong ClSeq { get; set; } } /// /// Tracks the in-progress application of a committed batch on the Raft apply path. /// Mirrors batchApply in server/jetstream_batching.go. /// internal sealed class BatchApply { private readonly Lock _mu = new(); /// ID of the current batch. public string Id { get; set; } = string.Empty; /// Number of entries expected in the batch (for consistency checks). public ulong Count { get; set; } /// Raft committed entries that make up this batch. public List? Entries { get; set; } // []*CommittedEntry — session 20+ /// Index within an entry indicating the first message of the batch. public int EntryStart { get; set; } /// Applied value before the entry containing the first batch message. public ulong MaxApplied { get; set; } public Lock Mu => _mu; /// /// Clears in-memory apply-batch state. /// Mirrors batchApply.clearBatchStateLocked. /// Lock should be held. /// public void ClearBatchStateLocked() { Id = string.Empty; Count = 0; Entries = null; EntryStart = 0; MaxApplied = 0; } }