perf: batch flush + signal-based wakeup for JS pull consumers
Replace per-message DeliverMessage/flush in DeliverPullFetchMessagesAsync with SendMessageNoFlush + batch flush every 64 messages. Add signal-based wakeup (StreamHandle.NotifyPublish/WaitForPublishAsync) to replace 5ms Task.Delay polling in both DeliverPullFetchMessagesAsync and PullConsumerEngine.WaitForMessageAsync. Publishers signal waiting consumers immediately after store append.
This commit is contained in:
@@ -457,6 +457,9 @@ public sealed class StreamManager : IDisposable
|
||||
var seq = stream.Store.AppendAsync(storeSubject, payload, default).GetAwaiter().GetResult();
|
||||
EnforceRuntimePolicies(stream, DateTime.UtcNow);
|
||||
|
||||
// Wake up any pull consumers waiting at the stream tail.
|
||||
stream.NotifyPublish();
|
||||
|
||||
// Only load the stored message when replication is configured (mirror/source).
|
||||
// Avoids unnecessary disk I/O on the hot publish path.
|
||||
if (_mirrorsByOrigin.ContainsKey(stream.Config.Name) || _sourcesByOrigin.ContainsKey(stream.Config.Name))
|
||||
@@ -507,6 +510,9 @@ public sealed class StreamManager : IDisposable
|
||||
var seq = stream.Store.AppendAsync(storeSubject, newPayload, default).GetAwaiter().GetResult();
|
||||
EnforceRuntimePolicies(stream, DateTime.UtcNow);
|
||||
|
||||
// Wake up any pull consumers waiting at the stream tail.
|
||||
stream.NotifyPublish();
|
||||
|
||||
if (_mirrorsByOrigin.ContainsKey(stream.Config.Name) || _sourcesByOrigin.ContainsKey(stream.Config.Name))
|
||||
{
|
||||
var stored = stream.Store.LoadAsync(seq, default).GetAwaiter().GetResult();
|
||||
@@ -961,4 +967,25 @@ public sealed class StreamManager : IDisposable
|
||||
}
|
||||
}
|
||||
|
||||
public sealed record StreamHandle(StreamConfig Config, IStreamStore Store);
|
||||
public sealed record StreamHandle(StreamConfig Config, IStreamStore Store)
|
||||
{
|
||||
// Signal-based wakeup for pull consumers waiting at the stream tail.
|
||||
// Go reference: consumer.go — channel signaling from publisher to waiting consumer.
|
||||
private volatile TaskCompletionSource _publishSignal = new(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||
|
||||
/// <summary>
|
||||
/// Notifies waiting consumers that a new message has been published.
|
||||
/// </summary>
|
||||
public void NotifyPublish()
|
||||
{
|
||||
var old = Interlocked.Exchange(ref _publishSignal,
|
||||
new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously));
|
||||
old.TrySetResult();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Waits until a new message is published to this stream.
|
||||
/// </summary>
|
||||
public Task WaitForPublishAsync(CancellationToken ct)
|
||||
=> _publishSignal.Task.WaitAsync(ct);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user