Example #1
0
        /// <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);
            }
        }
Example #2
0
        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);
            }
        }
Example #3
0
        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();
            }
        }
Example #4
0
        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);
            }
        }
Example #5
0
        /// <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);
            }
        }
Example #6
0
        /// <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);
            }
        }
Example #7
0
        /// <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);
                }
            }
        }