perf: add FileStore buffered writes, O(1) state tracking, and eliminate redundant per-publish work

Implement Go-parity background flush loop (coalesce 16KB/8ms) in MsgBlock/FileStore,
replace O(n) GetStateAsync with incremental counters, skip PruneExpired/LoadAsync/
PrunePerSubject when not needed, and bypass RAFT for single-replica streams. Fix counter
tracking bugs in RemoveMsg/EraseMsg/TTL expiry and ObjectDisposedException races in
flush loop disposal. FileStore optimizations verified with 3112/3112 JetStream tests
passing; async publish benchmark remains at ~174 msg/s due to E2E protocol path bottleneck.
This commit is contained in:
Joseph Doherty
2026-03-13 03:11:11 -04:00
parent 37575dc41c
commit 4de691c9c5
30 changed files with 1514 additions and 185 deletions

View File

@@ -0,0 +1,42 @@
using System.Text.Json;
using System.Text.Json.Serialization;
namespace NATS.Server.JetStream.Models;
// Go: server/stream.go — counterMsgPayload struct { Value string `json:"val"` }
// Reference: golang/nats-server/server/stream.go:20759
public sealed class CounterValue
{
[JsonPropertyName("val")]
public string Value { get; set; } = "0";
public long AsLong() => long.TryParse(Value, out var v) ? v : 0;
public static CounterValue FromLong(long value) => new() { Value = value.ToString() };
public byte[] ToPayload() => JsonSerializer.SerializeToUtf8Bytes(this);
public static CounterValue FromPayload(ReadOnlySpan<byte> payload)
{
if (payload.IsEmpty)
return new CounterValue();
try
{
return JsonSerializer.Deserialize<CounterValue>(payload) ?? new CounterValue();
}
catch (JsonException)
{
return new CounterValue();
}
}
}
// Go: NATS header constants for counter messages.
// Reference: golang/nats-server/server/stream.go — counter header constants.
public static class CounterHeaders
{
public const string NatsIncr = "Nats-Incr";
public const string NatsCounterSources = "Nats-Counter-Sources";
public const string NatsStreamSource = "Nats-Stream-Source";
}