Exemple #1
0
            private async Task CleanQueue(bool checkEntireQueueOnCleanup)
            {
                long totalCleanupCount      = 0;
                long totalCleanupStoreCount = 0;

                while (true)
                {
                    foreach (KeyValuePair <string, ISequentialStore <MessageRef> > endpointSequentialStore in this.messageStore.endpointSequentialStores)
                    {
                        var messageQueueId = endpointSequentialStore.Key;
                        try
                        {
                            if (this.cancellationTokenSource.IsCancellationRequested)
                            {
                                return;
                            }

                            var(endpointId, priority) = MessageQueueIdHelper.ParseMessageQueueId(messageQueueId);
                            Events.CleanupTaskStarted(messageQueueId);
                            CheckpointData checkpointData = await this.messageStore.checkpointStore.GetCheckpointDataAsync(messageQueueId, CancellationToken.None);

                            ISequentialStore <MessageRef> sequentialStore = endpointSequentialStore.Value;
                            Events.CleanupCheckpointState(messageQueueId, checkpointData);
                            int cleanupEntityStoreCount = 0;

                            // If checkEntireQueueOnCleanup is set to false, we only peek the head, message counts is tailOffset-headOffset+1
                            // otherwise count while iterating over the queue.
                            var headOffset   = 0L;
                            var tailOffset   = sequentialStore.GetTailOffset(CancellationToken.None);
                            var messageCount = 0L;

                            async Task <bool> DeleteMessageCallback(long offset, MessageRef messageRef)
                            {
                                var expiry = messageRef.TimeStamp + messageRef.TimeToLive;

                                if (offset > checkpointData.Offset && expiry > DateTime.UtcNow)
                                {
                                    // message is not sent and not expired, increase message counts
                                    messageCount++;
                                    return(false);
                                }

                                headOffset = Math.Max(headOffset, offset);
                                bool deleteMessage = false;

                                // Decrement ref count.
                                var message = await this.messageStore.messageEntityStore.Update(
                                    messageRef.EdgeMessageId,
                                    m =>
                                {
                                    if (m.RefCount > 0)
                                    {
                                        m.RefCount--;
                                    }

                                    if (m.RefCount == 0)
                                    {
                                        deleteMessage = true;
                                    }

                                    return(m);
                                });

                                if (deleteMessage)
                                {
                                    if (offset > checkpointData.Offset && expiry <= DateTime.UtcNow)
                                    {
                                        this.expiredCounter.Increment(1, new[] { "ttl_expiry", message?.Message.GetSenderId(), message?.Message.GetOutput(), bool.TrueString });
                                    }

                                    await this.messageStore.messageEntityStore.Remove(messageRef.EdgeMessageId);

                                    cleanupEntityStoreCount++;
                                }

                                return(true);
                            }

                            // With the addition of PriorityQueues, the CleanupProcessor assumptions change slightly:
                            // Previously, we could always assume that if a message at the head of the queue should not be deleted,
                            // then none of the other messages in the queue should be either. Now, because we can have different TTL's
                            // for messages within the same queue, there can be messages that have expired in the queue after the head.
                            // The checkEntireQueueOnCleanup flag is an environment variable for edgeHub. If it is set to true, we will
                            // check the entire queue every time cleanup processor runs. If it is set to false, we just remove the oldest
                            // items in the queue until we get to one that is not expired.
                            int cleanupCount = 0;
                            if (checkEntireQueueOnCleanup)
                            {
                                IEnumerable <(long, MessageRef)> batch;
                                long offset = sequentialStore.GetHeadOffset(this.cancellationTokenSource.Token);
                                do
                                {
                                    batch = await sequentialStore.GetBatch(offset, CleanupBatchSize);

                                    foreach ((long, MessageRef)messageWithOffset in batch)
                                    {
                                        if (await sequentialStore.RemoveOffset(DeleteMessageCallback, messageWithOffset.Item1, this.cancellationTokenSource.Token))
                                        {
                                            cleanupCount++;
                                        }
                                    }

                                    offset += CleanupBatchSize;
                                }while (batch.Any());
                            }
                            else
                            {
                                while (await sequentialStore.RemoveFirst(DeleteMessageCallback))
                                {
                                    cleanupCount++;
                                }

                                messageCount = tailOffset - headOffset + 1;
                            }

                            // update Metrics for message counts
                            Checkpointer.Metrics.QueueLength.Set(messageCount, new[] { endpointId, priority.ToString(), bool.TrueString });
                            totalCleanupCount      += cleanupCount;
                            totalCleanupStoreCount += cleanupEntityStoreCount;
                            Events.CleanupCompleted(messageQueueId, cleanupCount, cleanupEntityStoreCount, totalCleanupCount, totalCleanupStoreCount);
                            await Task.Delay(MinCleanupSleepTime, this.cancellationTokenSource.Token);
                        }
                        catch (Exception ex)
                        {
                            Events.ErrorCleaningMessagesForEndpoint(ex, messageQueueId);
                        }
                    }

                    await Task.Delay(this.GetCleanupTaskSleepTime());
                }
            }
Exemple #2
0
            private async Task CleanQueue(bool checkEntireQueueOnCleanup)
            {
                long totalCleanupCount      = 0;
                long totalCleanupStoreCount = 0;

                while (true)
                {
                    foreach (KeyValuePair <string, ISequentialStore <MessageRef> > endpointSequentialStore in this.messageStore.endpointSequentialStores)
                    {
                        try
                        {
                            if (this.cancellationTokenSource.IsCancellationRequested)
                            {
                                return;
                            }

                            Events.CleanupTaskStarted(endpointSequentialStore.Key);
                            CheckpointData checkpointData = await this.messageStore.checkpointStore.GetCheckpointDataAsync(endpointSequentialStore.Key, CancellationToken.None);

                            ISequentialStore <MessageRef> sequentialStore = endpointSequentialStore.Value;
                            Events.CleanupCheckpointState(endpointSequentialStore.Key, checkpointData);
                            int cleanupEntityStoreCount = 0;

                            async Task <bool> DeleteMessageCallback(long offset, MessageRef messageRef)
                            {
                                if (checkpointData.Offset < offset &&
                                    DateTime.UtcNow - messageRef.TimeStamp < messageRef.TimeToLive)
                                {
                                    return(false);
                                }

                                bool deleteMessage = false;

                                // Decrement ref count.
                                await this.messageStore.messageEntityStore.Update(
                                    messageRef.EdgeMessageId,
                                    m =>
                                {
                                    if (m.RefCount > 0)
                                    {
                                        m.RefCount--;
                                    }

                                    if (m.RefCount == 0)
                                    {
                                        deleteMessage = true;
                                    }

                                    return(m);
                                });

                                if (deleteMessage)
                                {
                                    await this.messageStore.messageEntityStore.Remove(messageRef.EdgeMessageId);

                                    cleanupEntityStoreCount++;
                                }

                                return(true);
                            }

                            // With the addition of PriorityQueues, the CleanupProcessor assumptions change slightly:
                            // Previously, we could always assume that if a message at the head of the queue should not be deleted,
                            // then none of the other messages in the queue should be either. Now, because we can have different TTL's
                            // for messages within the same queue, there can be messages that have expired in the queue after the head.
                            // The checkEntireQueueOnCleanup flag is an environment variable for edgeHub. If it is set to true, we will
                            // check the entire queue every time cleanup processor runs. If it is set to false, we just remove the oldest
                            // items in the queue until we get to one that is not expired.
                            int cleanupCount = 0;
                            if (checkEntireQueueOnCleanup)
                            {
                                IEnumerable <(long, MessageRef)> batch;
                                long offset = sequentialStore.GetHeadOffset(this.cancellationTokenSource.Token);
                                do
                                {
                                    batch = await sequentialStore.GetBatch(offset, CleanupBatchSize);

                                    foreach ((long, MessageRef)messageWithOffset in batch)
                                    {
                                        if (await sequentialStore.RemoveOffset(DeleteMessageCallback, messageWithOffset.Item1, this.cancellationTokenSource.Token))
                                        {
                                            cleanupCount++;
                                        }
                                    }

                                    offset = offset + CleanupBatchSize;
                                }while (batch.Any());
                            }
                            else
                            {
                                while (await sequentialStore.RemoveFirst(DeleteMessageCallback))
                                {
                                    cleanupCount++;
                                }
                            }

                            totalCleanupCount      += cleanupCount;
                            totalCleanupStoreCount += cleanupEntityStoreCount;
                            Events.CleanupCompleted(endpointSequentialStore.Key, cleanupCount, cleanupEntityStoreCount, totalCleanupCount, totalCleanupStoreCount);
                            await Task.Delay(MinCleanupSleepTime, this.cancellationTokenSource.Token);
                        }
                        catch (Exception ex)
                        {
                            Events.ErrorCleaningMessagesForEndpoint(ex, endpointSequentialStore.Key);
                        }
                    }

                    await Task.Delay(this.GetCleanupTaskSleepTime());
                }
            }