/// <summary> /// Try to dequeue the oldest message that obeys the defined delivery policy. /// </summary> /// <param name="message">The oldest message if it exists, or default otherwise.</param> /// <param name="stateTransition">Struct that describes the status of the internal queue after the dequeue.</param> /// <param name="currentTime">The current time of the pipeline that is used to calculate latency.</param> /// <returns>True if oldest message that satisfies the policy is found.</returns> public bool TryDequeue(out Message <T> message, out QueueTransition stateTransition, DateTime currentTime) { message = default(Message <T>); bool found = false; lock (this.queue) { // loop through the queue while (this.queue.Count != 0) { var oldest = this.queue.Dequeue(); // check if we have a maximum-latency policy and a message that exceeds that latency if (this.policy.MaximumLatency.HasValue && (currentTime - oldest.OriginatingTime) > this.policy.MaximumLatency.Value) { // Because this message has a latency that is larger than the policy allowed, we are dropping this message and // finding the next message with smaller latency. this.pipeline.DiagnosticsCollector?.MessageDropped(this.pipeline, this.element, this.receiver, this.Count, message.Envelope); this.cloner.Recycle(oldest.Data); this.counters?.Increment(ReceiverCounters.Dropped); } else { message = oldest; found = true; break; } } this.counters?.RawValue(ReceiverCounters.QueueSize, this.queue.Count); stateTransition = this.UpdateState(); return(found); } }
public bool TryDequeue(out Message <T> message, out QueueTransition stateTransition, DateTime currentTime) { message = default(Message <T>); bool found = false; lock (this.queue) { while (this.queue.Count != 0) { var oldest = this.queue.Dequeue(); // check if we have a maximum-latency policy and a message that exceeds that latency if (this.policy.MaximumLatency.HasValue && (currentTime - oldest.OriginatingTime) > this.policy.MaximumLatency.Value) { this.cloner.Recycle(oldest.Data); this.counters?.Increment(ReceiverCounters.Dropped); } else { message = oldest; found = true; break; } } this.counters?.RawValue(ReceiverCounters.QueueSize, this.queue.Count); stateTransition = this.UpdateState(); return(found); } }
public void Enqueue(Message <T> message, out QueueTransition stateTransition) { lock (this.queue) { if (this.queue.Count > this.policy.MaximumQueueSize) { var item = this.queue.Dequeue(); // discard unprocessed items if the policy requires it this.cloner.Recycle(item.Data); this.counters?.Increment(ReceiverCounters.Dropped); } this.queue.Enqueue(message); if (this.queue.Count > this.maxQueueSize) { this.maxQueueSize = this.queue.Count; } if (this.counters != null) { this.counters.RawValue(ReceiverCounters.QueueSize, this.queue.Count); this.counters.RawValue(ReceiverCounters.MaxQueueSize, this.maxQueueSize); } stateTransition = this.UpdateState(); } }
public bool TryDequeue(out Message <T> message, out QueueTransition stateTransition, DateTime currentTime) { message = default(Message <T>); bool found = false; lock (this.queue) { while (this.queue.Count != 0) { var oldest = this.queue.Dequeue(); if (this.IsExpired(oldest.OriginatingTime, currentTime)) { this.cloner.Recycle(oldest.Data); this.counters?.Increment(ReceiverCounters.Dropped); } else { message = oldest; found = true; break; } } this.counters?.RawValue(ReceiverCounters.QueueSize, this.queue.Count); stateTransition = this.UpdateState(); return(found); } }
/// <summary> /// Enqueue the message while respecting the defined delivery policy. /// </summary> /// <param name="message">The new message.</param> /// <param name="stateTransition">The struct describing the status of the internal queue.</param> public void Enqueue(Message <T> message, out QueueTransition stateTransition) { lock (this.queue) { // If the queue size is more than the allowed size in the policy, recycle the oldest object in the queue. if (this.queue.Count >= this.policy.MaximumQueueSize) { var item = this.queue.Dequeue(); // discard unprocessed items if the policy requires it this.pipeline.DiagnosticsCollector?.MessageDropped(this.pipeline, this.element, this.receiver, this.Count, item.Envelope); this.cloner.Recycle(item.Data); this.counters?.Increment(ReceiverCounters.Dropped); } // special closing message if (message.SequenceId == int.MaxValue) { // queued messages with an originating time past the closing time should be dropped while (this.queue.Count > 0 && this.queue.Peek().OriginatingTime > message.OriginatingTime) { var item = this.queue.Dequeue(); // discard unprocessed items which occur after the closing message this.pipeline.DiagnosticsCollector?.MessageDropped(this.pipeline, this.element, this.receiver, this.Count, item.Envelope); this.cloner.Recycle(item.Data); this.counters?.Increment(ReceiverCounters.Dropped); } } // enqueue the new message this.queue.Enqueue(message); // Update a bunch of variables that helps with diagnostics and performance measurement. if (this.queue.Count > this.maxQueueSize) { this.maxQueueSize = this.queue.Count; } if (this.counters != null) { this.counters.RawValue(ReceiverCounters.QueueSize, this.queue.Count); this.counters.RawValue(ReceiverCounters.MaxQueueSize, this.maxQueueSize); } // computes the new state that indicates the status of the internal queue and whether the queue needs to be locked and expanded. stateTransition = this.UpdateState(); this.pipeline.DiagnosticsCollector?.MessageEnqueued(this.pipeline, this.element, this.receiver, this.Count, message.Envelope); } }
/// <summary> /// Try to dequeue the oldest message that obeys the defined delivery policy. /// </summary> /// <param name="message">The oldest message if it exists, or default otherwise.</param> /// <param name="stateTransition">Struct that describes the status of the internal queue after the dequeue.</param> /// <param name="currentTime">The current time of the pipeline that is used to calculate latency.</param> /// <param name="receiverCollector">Diagnostics collector for this receiver.</param> /// <param name="stateTransitionAction">Action to perform after the queue state transition has been evaluated.</param> /// <returns>True if oldest message that satisfies the policy is found.</returns> public bool TryDequeue(out Message <T> message, out QueueTransition stateTransition, DateTime currentTime, DiagnosticsCollector.ReceiverCollector receiverCollector, Action <QueueTransition> stateTransitionAction) { message = default; bool found = false; lock (this.queue) { // loop through the queue while (this.queue.Count != 0) { var oldest = this.queue.Dequeue(); // compute whether we should drop this message, i.e. if the message exceeds the maximum latency and it // does not have guaranteed delivery var shouldDrop = this.policy.MaximumLatency.HasValue && (currentTime - oldest.OriginatingTime) > this.policy.MaximumLatency.Value && ((this.policy.GuaranteeDelivery == null) || !this.policy.GuaranteeDelivery(oldest.Data)); if (shouldDrop) { // Because this message has a latency that is larger than the policy allowed, we are dropping this message and // finding the next message with smaller latency. receiverCollector?.MessageDropped(currentTime); this.cloner.Recycle(oldest.Data); this.counters?.Increment(ReceiverCounters.Dropped); } else { message = oldest; found = true; break; } } this.counters?.RawValue(ReceiverCounters.QueueSize, this.queue.Count); stateTransition = this.UpdateState(); stateTransitionAction?.Invoke(stateTransition); receiverCollector?.QueueSizeUpdate(this.queue.Count, currentTime); return(found); } }
/// <summary> /// Enqueue the message while respecting the defined delivery policy. /// </summary> /// <param name="message">The new message.</param> /// <param name="stateTransition">The struct describing the status of the internal queue.</param> /// <param name="receiverCollector">Diagnostics collector for this receiver.</param> /// <param name="stateTransitionAction">Action to perform after the queue state transition has been evaluated.</param> public void Enqueue(Message <T> message, out QueueTransition stateTransition, DiagnosticsCollector.ReceiverCollector receiverCollector, Action <QueueTransition> stateTransitionAction) { lock (this.queue) { var dropIncomingMessage = false; // If the queue size is more than the allowed size in the policy, try to drop messages in the queue // if possible, until we create enough space to hold the new message if (this.queue.Count >= this.policy.MaximumQueueSize) { if (this.policy.GuaranteeDelivery == null) { // if we have no guarantees on the policy, then just drop the oldest message var item = this.queue.Dequeue(); receiverCollector?.MessageDropped(this.Count, message.Envelope); this.cloner.Recycle(item.Data); this.counters?.Increment(ReceiverCounters.Dropped); } else { // if we have a policy with delivery guarantees, we need to go through the queue // and inspect which messages can be removed. // if the queue has already grown to a size strictly larger that MaximumQueueSize if (this.queue.Count > this.policy.MaximumQueueSize) { // that means all messages in the queue have guaranteed delivery and cannot // be dropped. In this case, we avoid further growing the queue by simply // dropping the incoming message if it does not have guaranteed delivery dropIncomingMessage = !this.policy.GuaranteeDelivery(message.Data); } else { // otherwise, the queue has exactly the maximum size, in which case we need // to look through it to find whether there is a non-guaranteed message that // we can drop. Start by traversing the queue and moving the messages into // a temporary queue. Once we find a first message that we can free, stop // the traversal. // There is also an optimization here where instead of using messages into the // a temporary queue, we move them into the same queue, wrapping around. We do // a for loop that traverses the entire queue, taking each element and re-adding // it to the queue (with the exception of the element that we drop, if we find // one that can be dropped). var queueSize = this.queue.Count; // the hasDropped variable indicates if a message was already dropped in the // traversal. bool hasDropped = false; for (int i = 0; i < queueSize; i++) { // get the top item var item = this.queue.Dequeue(); // if no messages has been dropped yet and this message can be dropped if (!hasDropped && !this.policy.GuaranteeDelivery(item.Data)) { // then drop it receiverCollector?.MessageDropped(this.Count, message.Envelope); this.cloner.Recycle(item.Data); this.counters?.Increment(ReceiverCounters.Dropped); hasDropped = true; } else { // o/w push it back at the end of the queue this.queue.Enqueue(item); } } // if at this point we haven't found something to drop, we should drop the // incoming message, if it does not have guaranteed delivery dropIncomingMessage = !hasDropped && !this.policy.GuaranteeDelivery(message.Data); } } } var isClosingMessage = message.SequenceId == int.MaxValue; // special closing message if (isClosingMessage) { // queued messages with an originating time past the closing time should be dropped, regardless // of their guaranteed delivery. while (this.queue.Count > 0 && this.queue.Peek().OriginatingTime > message.OriginatingTime) { var item = this.queue.Dequeue(); // discard unprocessed items which occur after the closing message receiverCollector?.MessageDropped(this.Count, item.Envelope); this.cloner.Recycle(item.Data); this.counters?.Increment(ReceiverCounters.Dropped); } } // enqueue the new message, if it's the closing one, or if we should not drop if (!dropIncomingMessage || isClosingMessage) { this.queue.Enqueue(message); } // Update a bunch of variables that helps with diagnostics and performance measurement. if (this.queue.Count > this.maxQueueSize) { this.maxQueueSize = this.queue.Count; } if (this.counters != null) { this.counters.RawValue(ReceiverCounters.QueueSize, this.queue.Count); this.counters.RawValue(ReceiverCounters.MaxQueueSize, this.maxQueueSize); } // computes the new state that indicates the status of the internal queue and whether the queue needs to be locked and expanded. stateTransition = this.UpdateState(); stateTransitionAction?.Invoke(stateTransition); if (!dropIncomingMessage || isClosingMessage) { receiverCollector?.MessageEnqueued(this.Count, message.Envelope); } } }