namespace NATS.Server.JetStream.Consumers; /// /// A pull request with mutable batch and byte tracking for delivery fulfillment. /// Go reference: consumer.go waitingRequest / processNextMsgRequest. /// public sealed record PullRequest( string ReplyTo, int Batch, long MaxBytes, DateTimeOffset Expires, bool NoWait, string? PinId = null) { /// Remaining messages allowed for this request. public int RemainingBatch { get; private set; } = Batch; /// Remaining bytes allowed for this request (only meaningful when MaxBytes > 0). public long RemainingBytes { get; private set; } = MaxBytes; /// True when batch or bytes (if set) are exhausted. public bool IsExhausted => RemainingBatch <= 0 || (MaxBytes > 0 && RemainingBytes <= 0); /// Decrement remaining batch count by one. public void ConsumeBatch() => RemainingBatch--; /// Subtract delivered bytes from remaining byte budget. public void ConsumeBytes(long bytes) => RemainingBytes -= bytes; } /// /// FIFO queue of pull requests with expiry support. /// Unlike PullRequestWaitQueue (priority-based), this is a simple FIFO with /// RemoveExpired cleanup and mutable request tracking. /// Go reference: consumer.go waitQueue / processNextMsgRequest. /// public sealed class WaitingRequestQueue { private readonly LinkedList _queue = new(); public int Count => _queue.Count; public bool IsEmpty => _queue.Count == 0; public void Enqueue(PullRequest request) => _queue.AddLast(request); public PullRequest? TryDequeue() { if (_queue.Count == 0) return null; var first = _queue.First!.Value; _queue.RemoveFirst(); return first; } public void RemoveExpired(DateTimeOffset now) { var node = _queue.First; while (node != null) { var next = node.Next; if (node.Value.Expires <= now) _queue.Remove(node); node = next; } } }