feat: port session 01 — Foundation Types (const, errors, proto, ring, rate_counter, sdm)
Ports server/const.go, errors.go, proto.go, ring.go, rate_counter.go, sdm.go. - ServerConstants: all protocol constants and version info from const.go - ServerErrors: ~60 sentinel exceptions plus errCtx/configErr/processConfigErr types - ProtoWire: protobuf varint encode/decode helpers (proto.go) - RateCounter: sliding-window rate limiter (rate_counter.go) - ClosedRingBuffer: fixed-size ring buffer for /connz (ring.go) - StreamDeletionMeta: SDM tracking for JetStream cluster consensus (sdm.go) - 5 unit tests passing (errors, ring buffer, rate counter) - errors_gen.go (code generator tool) and nkey.go Server methods marked n_a
This commit is contained in:
@@ -0,0 +1,121 @@
|
||||
// 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/sdm.go in the NATS server Go source.
|
||||
|
||||
namespace ZB.MOM.NatsNet.Server.Internal.DataStructures;
|
||||
|
||||
/// <summary>
|
||||
/// Per-sequence data tracked by <see cref="StreamDeletionMeta"/>.
|
||||
/// Mirrors <c>SDMBySeq</c> in server/sdm.go.
|
||||
/// </summary>
|
||||
public readonly struct SdmBySeq
|
||||
{
|
||||
/// <summary>Whether this sequence was the last message for its subject.</summary>
|
||||
public bool Last { get; init; }
|
||||
|
||||
/// <summary>Timestamp (nanoseconds UTC) when the removal/SDM was last proposed.</summary>
|
||||
public long Ts { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tracks pending subject delete markers (SDMs) and message removals for a stream.
|
||||
/// Used by JetStream cluster consensus to avoid redundant proposals.
|
||||
/// Mirrors <c>SDMMeta</c> in server/sdm.go.
|
||||
/// </summary>
|
||||
public sealed class StreamDeletionMeta
|
||||
{
|
||||
// Per-subject pending-count totals.
|
||||
private readonly Dictionary<string, ulong> _totals = new(1);
|
||||
|
||||
// Per-sequence data keyed by sequence number.
|
||||
private readonly Dictionary<ulong, SdmBySeq> _pending = new(1);
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Header constants (forward-declared; populated in session 19 — JetStream).
|
||||
// isSubjectDeleteMarker checks these header keys.
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
// Mirrors JSMarkerReason header key (defined in jetstream.go).
|
||||
internal const string HeaderJsMarkerReason = "Nats-Marker-Reason";
|
||||
|
||||
// Mirrors KVOperation header key (defined in jetstream.go).
|
||||
internal const string HeaderKvOperation = "KV-Operation";
|
||||
|
||||
// Mirrors KVOperationValuePurge (defined in jetstream.go).
|
||||
internal const string KvOperationValuePurge = "PURGE";
|
||||
|
||||
/// <summary>
|
||||
/// Returns true when the given header block contains a subject delete marker
|
||||
/// (either a JetStream marker or a KV purge operation).
|
||||
/// Mirrors <c>isSubjectDeleteMarker</c> in server/sdm.go.
|
||||
/// </summary>
|
||||
public static bool IsSubjectDeleteMarker(ReadOnlySpan<byte> hdr)
|
||||
{
|
||||
// Simplified header scan: checks whether JSMarkerReason key is present
|
||||
// or whether KV-Operation equals "PURGE".
|
||||
// Full implementation depends on SliceHeader from session 08 (client.go).
|
||||
// Until then this provides the correct contract.
|
||||
var text = System.Text.Encoding.UTF8.GetString(hdr);
|
||||
if (text.Contains(HeaderJsMarkerReason))
|
||||
return true;
|
||||
if (text.Contains($"{HeaderKvOperation}: {KvOperationValuePurge}"))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears all tracked data.
|
||||
/// Mirrors <c>SDMMeta.empty</c>.
|
||||
/// </summary>
|
||||
public void Empty()
|
||||
{
|
||||
_totals.Clear();
|
||||
_pending.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tracks <paramref name="seq"/> as pending and returns whether it was
|
||||
/// the last message for its subject. If the sequence is already tracked
|
||||
/// the existing <c>Last</c> value is returned without modification.
|
||||
/// Mirrors <c>SDMMeta.trackPending</c>.
|
||||
/// </summary>
|
||||
public bool TrackPending(ulong seq, string subj, bool last)
|
||||
{
|
||||
if (_pending.TryGetValue(seq, out var p))
|
||||
return p.Last;
|
||||
|
||||
_pending[seq] = new SdmBySeq { Last = last, Ts = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds() * 1_000_000L };
|
||||
_totals[subj] = _totals.TryGetValue(subj, out var cnt) ? cnt + 1 : 1;
|
||||
return last;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes <paramref name="seq"/> and decrements the pending count for
|
||||
/// <paramref name="subj"/>, deleting the subject entry when it reaches zero.
|
||||
/// Mirrors <c>SDMMeta.removeSeqAndSubject</c>.
|
||||
/// </summary>
|
||||
public void RemoveSeqAndSubject(ulong seq, string subj)
|
||||
{
|
||||
if (!_pending.Remove(seq))
|
||||
return;
|
||||
|
||||
if (_totals.TryGetValue(subj, out var msgs))
|
||||
{
|
||||
if (msgs <= 1)
|
||||
_totals.Remove(subj);
|
||||
else
|
||||
_totals[subj] = msgs - 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user