perf: Phase 1 JetStream async file publish optimizations
- Add cached state properties (LastSeq, MessageCount, TotalBytes, FirstSeq) to IStreamStore/FileStore/MemStore — eliminates GetStateAsync on publish path - Add Capture(StreamHandle, ...) overload to StreamManager — eliminates double FindBySubject lookup (once in JetStreamPublisher, once in Capture) - Remove _messageIndexes dictionary from FileStore write path — all lookups now use _messages directly, saving ~48B allocation per message - Add JetStreamPubAckFormatter for hand-rolled UTF-8 success ack formatting — avoids JsonSerializer overhead on the hot publish path - Switch flush loop to exponential backoff (1→2→4→8ms) matching Go server
This commit is contained in:
@@ -37,8 +37,8 @@ public sealed class JetStreamPublisher
|
||||
}
|
||||
|
||||
// --- Normal (non-batch) publish path ---
|
||||
var state = stream.Store.GetStateAsync(default).GetAwaiter().GetResult();
|
||||
if (!_preconditions.CheckExpectedLastSeq(options.ExpectedLastSeq, state.LastSeq))
|
||||
// Use cached LastSeq property instead of GetStateAsync to avoid allocation.
|
||||
if (!_preconditions.CheckExpectedLastSeq(options.ExpectedLastSeq, stream.Store.LastSeq))
|
||||
{
|
||||
ack = new PubAck { ErrorCode = 10071 };
|
||||
return true;
|
||||
@@ -54,7 +54,8 @@ public sealed class JetStreamPublisher
|
||||
return true;
|
||||
}
|
||||
|
||||
var captured = _streamManager.Capture(subject, payload);
|
||||
// Pass resolved stream to avoid double FindBySubject lookup.
|
||||
var captured = _streamManager.Capture(stream, subject, payload);
|
||||
ack = captured ?? new PubAck();
|
||||
_preconditions.Record(options.MsgId, ack.Seq);
|
||||
_preconditions.TrimOlderThan(stream.Config.DuplicateWindowMs);
|
||||
@@ -136,15 +137,14 @@ public sealed class JetStreamPublisher
|
||||
stream.Config.DuplicateWindowMs,
|
||||
staged =>
|
||||
{
|
||||
// Check expected last sequence.
|
||||
// Check expected last sequence using cached property.
|
||||
if (staged.ExpectedLastSeq > 0)
|
||||
{
|
||||
var st = stream.Store.GetStateAsync(default).GetAwaiter().GetResult();
|
||||
if (st.LastSeq != staged.ExpectedLastSeq)
|
||||
if (stream.Store.LastSeq != staged.ExpectedLastSeq)
|
||||
return new PubAck { ErrorCode = 10071, Stream = stream.Config.Name };
|
||||
}
|
||||
|
||||
var captured = _streamManager.Capture(staged.Subject, staged.Payload);
|
||||
var captured = _streamManager.Capture(stream, staged.Subject, staged.Payload);
|
||||
return captured ?? new PubAck { Stream = stream.Config.Name };
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user