/// <summary>
        /// Takes a published message and forwards it (indirectly) to all Subscribers.
        /// </summary>
        /// <param name="message">The message to publish</param>
        /// <returns></returns>
        public async Task PublishMessageAsync(MessageWrapper message)
        {
            await WaitForInitializeAsync(CancellationToken.None);

            await _brokerEventsManager.OnMessagePublishedAsync(message);

            var myDictionary = await TimeoutRetryHelper.Execute((token, state) => StateManager.GetOrAddAsync <IReliableDictionary <string, BrokerServiceState> >(message.MessageType));

            var subscriptions = await TimeoutRetryHelper.ExecuteInTransaction(StateManager, async (tx, token, state) =>
            {
                var result = await myDictionary.TryGetValueAsync(tx, Subscribers);
                return(result.HasValue ? result.Value.Subscribers.ToArray() : null);
            });

            if (subscriptions == null || subscriptions.Length == 0)
            {
                return;
            }

            ServiceEventSourceMessage($"Publishing message '{message.MessageType}' to {subscriptions.Length} subscribers.");

            await TimeoutRetryHelper.ExecuteInTransaction(StateManager, async (tx, token, state) =>
            {
                foreach (var subscriptionDetails in subscriptions)
                {
                    await _subscriptions[subscriptionDetails.QueueName].EnqueueMessageAsync(tx, message);
                    await _brokerEventsManager.OnMessageQueuedToSubscriberAsync(subscriptionDetails.QueueName, subscriptionDetails.ServiceOrActorReference, message);
                }
                ServiceEventSourceMessage($"Published message '{message.MessageType}' to {subscriptions.Length} subscribers.");
            });
        }
        /// <summary>
        /// Unregisters a Service or Actor <paramref name="reference"/> as subscriber for messages of type <paramref name="messageTypeName"/>
        /// </summary>
        /// <param name="reference"></param>
        /// <param name="messageTypeName"></param>
        /// <returns></returns>
        public async Task UnsubscribeAsync(ReferenceWrapper reference, string messageTypeName)
        {
            await WaitForInitializeAsync(CancellationToken.None);

            var myDictionary = await TimeoutRetryHelper.Execute((token, state) => StateManager.GetOrAddAsync <IReliableDictionary <string, BrokerServiceState> >(messageTypeName));

            var queueName = CreateQueueName(reference, messageTypeName);

            await TimeoutRetryHelper.ExecuteInTransaction(StateManager, async (tx, token, state) =>
            {
                var subscribers = await myDictionary.TryGetValueAsync(tx, Subscribers, LockMode.Update);
                if (subscribers.HasValue)
                {
                    var newState = BrokerServiceState.RemoveSubscriber(subscribers.Value, reference);
                    await myDictionary.SetAsync(tx, Subscribers, newState);
                }


                await StateManager.RemoveAsync(tx, queueName);

                ServiceEventSourceMessage($"Unregistered subscriber: {reference.Name}");
                _queues.TryRemove(queueName, out reference);
                await BrokerEventsManager.OnUnsubscribedAsync(queueName, reference, messageTypeName);
            });
        }
        /// <summary>
        /// Registers a Service or Actor <paramref name="reference"/> as subscriber for messages of type <paramref name="messageTypeName"/>
        /// </summary>
        /// <param name="reference">Reference to the Service or Actor to register.</param>
        /// <param name="messageTypeName">Full type name of message object.</param>
        /// <returns></returns>
        public async Task SubscribeAsync(ReferenceWrapper reference, string messageTypeName)
        {
            await WaitForInitializeAsync(CancellationToken.None);

            var myDictionary = await TimeoutRetryHelper.Execute((token, state) => StateManager.GetOrAddAsync <IReliableDictionary <string, BrokerServiceState> >(messageTypeName));

            await TimeoutRetryHelper.ExecuteInTransaction(StateManager, async (tx, token, state) =>
            {
                var queueName = CreateQueueName(reference, messageTypeName);

                Func <string, BrokerServiceState> addValueFactory = key =>
                {
                    var newState   = new BrokerServiceState(messageTypeName);
                    var subscriber = new Reference(reference, queueName);
                    newState       = BrokerServiceState.AddSubscriber(newState, subscriber);
                    return(newState);
                };

                Func <string, BrokerServiceState, BrokerServiceState> updateValueFactory = (key, current) =>
                {
                    var subscriber = new Reference(reference, queueName);
                    var newState   = BrokerServiceState.AddSubscriber(current, subscriber);
                    return(newState);
                };

                await myDictionary.AddOrUpdateAsync(tx, Subscribers, addValueFactory, updateValueFactory);

                await CreateQueueAsync(tx, queueName);

                _queues.AddOrUpdate(queueName, reference, (key, old) => reference);
                ServiceEventSourceMessage($"Registered subscriber: {reference.Name}");
                await BrokerEventsManager.OnSubscribedAsync(queueName, reference, messageTypeName);
            }, cancellationToken : CancellationToken.None);
        }
        /// <summary>
        /// Loads all registered message queues from state and keeps them in memory. Avoids some locks in the statemanager.
        /// </summary>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        private async Task InitializeAsync(CancellationToken cancellationToken)
        {
            if (_initializer.IsSet)
            {
                return;
            }
            try
            {
                SetupEvents(_brokerEventsManager);
                _semaphore.Wait(cancellationToken);

                if (_initializer.IsSet)
                {
                    return;
                }
                await TimeoutRetryHelper.ExecuteInTransaction(StateManager, async (tx, token, state) =>
                {
                    _subscriptions.Clear();
                    var enumerator = StateManager.GetAsyncEnumerator();
                    while (await enumerator.MoveNextAsync(cancellationToken))
                    {
                        var current = enumerator.Current as IReliableDictionary <string, BrokerServiceState>;
                        if (current == null)
                        {
                            continue;
                        }


                        var result = await current.TryGetValueAsync(tx, Subscribers);
                        if (!result.HasValue)
                        {
                            continue;
                        }

                        var subscriptions = result.Value.Subscribers.ToList();
                        foreach (var subscriptionDetails in subscriptions)
                        {
                            var subscription = await _subscriptionFactory.CreateAsync(tx, subscriptionDetails);
                            _subscriptions.TryAdd(subscriptionDetails.QueueName, subscription);
                        }
                    }
                }, cancellationToken : cancellationToken);

                _initializer.Set();
            }
            finally
            {
                _semaphore.Release();
            }
        }
        /// <inheritdoc />
        public async Task ReceiveMessageAsync(MessageWrapper message)
        {
            //assume that message contains 'TaskDescription'
            var description = message.CreateMessage <TaskDescription>();

            if (description == null)
            {
                return;                      //wrong message
            }
            var queue = await TimeoutRetryHelper.Execute((token, state) => StateManager.GetOrAddAsync <IReliableQueue <MessageWrapper> >(_queueName));

            await TimeoutRetryHelper
            .ExecuteInTransaction(StateManager, (tran, token, state) => queue.EnqueueAsync(tran, message))
            .ConfigureAwait(false);
        }
        /// <summary>
        /// Registers a Service or Actor <paramref name="reference"/> as subscriber for messages of type <paramref name="messageTypeName"/>
        /// </summary>
        /// <param name="reference">Reference to the Service or Actor to register.</param>
        /// <param name="messageTypeName">Full type name of message object.</param>
        /// <param name="isOrdered"></param>
        /// <returns></returns>
        public async Task SubscribeAsync(ReferenceWrapper reference, string messageTypeName, bool isOrdered = true)
        {
            await WaitForInitializeAsync(CancellationToken.None);

            var brokerState = await TimeoutRetryHelper.Execute((token, state) => StateManager.GetOrAddAsync <IReliableDictionary <string, BrokerServiceState> >(messageTypeName));

            await TimeoutRetryHelper.ExecuteInTransaction(StateManager, async (tx, token, state) =>
            {
                var subscriptionDetails = new SubscriptionDetails(reference, messageTypeName, isOrdered);
                var subscription        = await _subscriptionFactory.CreateAsync(tx, subscriptionDetails);
                await brokerState.AddOrUpdateSubscription(tx, Subscribers, subscriptionDetails);

                _subscriptions.AddOrUpdate(subscriptionDetails.QueueName, subscription, (key, old) => subscription);
                ServiceEventSourceMessage($"Registered subscriber: {reference.Name}");
                await _brokerEventsManager.OnSubscribedAsync(subscriptionDetails.QueueName, reference, messageTypeName);
            }, cancellationToken : CancellationToken.None);
        }
        /// <summary>
        /// Unregisters a Service or Actor <paramref name="reference"/> as subscriber for messages of type <paramref name="messageTypeName"/>
        /// </summary>
        /// <param name="reference"></param>
        /// <param name="messageTypeName"></param>
        /// <returns></returns>
        public async Task UnsubscribeAsync(ReferenceWrapper reference, string messageTypeName)
        {
            await WaitForInitializeAsync(CancellationToken.None);

            var brokerState = await TimeoutRetryHelper.Execute((token, state) => StateManager.GetOrAddAsync <IReliableDictionary <string, BrokerServiceState> >(messageTypeName));

            await TimeoutRetryHelper.ExecuteInTransaction(StateManager, async (tx, token, state) =>
            {
                var queueName = SubscriptionDetails.CreateQueueName(reference, messageTypeName);
                if (_subscriptions.TryGetValue(queueName, out var subscription))
                {
                    await brokerState.RemoveSubscription(tx, Subscribers, subscription.SubscriptionDetails);
                }

                await StateManager.RemoveAsync(tx, queueName);

                ServiceEventSourceMessage($"Unregistered subscriber: {reference.Name}");
                _subscriptions.TryRemove(queueName, out _);
                await _brokerEventsManager.OnUnsubscribedAsync(queueName, reference, messageTypeName);
            });
        }
        /// <summary>
        /// Sends out queued messages for the provided queue.
        /// </summary>
        /// <param name="cancellationToken"></param>
        /// <param name="subscriber"></param>
        /// <param name="queueName"></param>
        /// <returns></returns>
        protected sealed override async Task ProcessQueues(CancellationToken cancellationToken, ReferenceWrapper subscriber, string queueName)
        {
            var queue = await TimeoutRetryHelper.Execute((token, state) => StateManager.GetOrAddAsync <IReliableConcurrentQueue <MessageWrapper> >(queueName), cancellationToken : cancellationToken);

            long messageCount = queue.Count;

            if (messageCount == 0L)
            {
                return;
            }
            messageCount = Math.Min(messageCount, MaxDequeuesInOneIteration);

            ServiceEventSourceMessage($"Processing {messageCount} items from queue {queue.Name} for subscriber: {subscriber.Name}");

            for (long i = 0; i < messageCount; i++)
            {
                cancellationToken.ThrowIfCancellationRequested();

                await TimeoutRetryHelper.ExecuteInTransaction(StateManager, async (tx, token, state) =>
                {
                    var result = await queue.TryDequeueAsync(tx, cancellationToken);
                    if (result.HasValue)
                    {
                        try
                        {
                            await subscriber.PublishAsync(result.Value);
                            await BrokerEventsManager.OnMessageDeliveredAsync(queueName, subscriber, result.Value);
                        }
                        catch (Exception ex)
                        {
                            await BrokerEventsManager.OnMessageDeliveryFailedAsync(queueName, subscriber, result.Value, ex);
                            throw;
                        }
                    }
                }, cancellationToken : cancellationToken);
            }
        }
        /// <summary>
        /// Sends out queued messages for the provided queue.
        /// </summary>
        /// <param name="cancellationToken"></param>
        /// <param name="subscription"></param>
        /// <returns></returns>
        private async Task ProcessQueues(CancellationToken cancellationToken, ISubscription subscription)
        {
            var details      = subscription.SubscriptionDetails;
            var messageCount = await subscription.GetQueueCount(cancellationToken);

            if (messageCount == 0L)
            {
                return;
            }
            messageCount = Math.Min(messageCount, MaxDequeuesInOneIteration);

            ServiceEventSourceMessage($"Processing {messageCount} items from queue {details.QueueName} for subscriber: {details.ServiceOrActorReference.Name}");

            for (long i = 0; i < messageCount; i++)
            {
                cancellationToken.ThrowIfCancellationRequested();

                await TimeoutRetryHelper.ExecuteInTransaction(StateManager, async (tx, token, state) =>
                {
                    var message = await subscription.DequeueMessageAsync(tx, cancellationToken);
                    if (message.HasValue)
                    {
                        try
                        {
                            await subscription.DeliverMessageAsync(message.Value, _proxyFactories);
                            await _brokerEventsManager.OnMessageDeliveredAsync(details.QueueName, details.ServiceOrActorReference, message.Value);
                        }
                        catch (Exception ex)
                        {
                            await _brokerEventsManager.OnMessageDeliveryFailedAsync(details.QueueName, details.ServiceOrActorReference, message.Value, ex, ThrottleFactor);
                            throw;
                        }
                    }
                }, cancellationToken : cancellationToken);
            }
        }
        /// <inheritdoc />
        protected override async Task RunAsync(CancellationToken cancellationToken)
        {
            var queue = await TimeoutRetryHelper.Execute(
                (token, state) => StateManager.GetOrAddAsync <IReliableQueue <MessageWrapper> >(_queueName),
                cancellationToken : cancellationToken)
                        .ConfigureAwait(false);

            while (true)
            {
                try
                {
                    cancellationToken.ThrowIfCancellationRequested();

                    //peek task description
                    var taskDescriptionMessage = await TimeoutRetryHelper
                                                 .ExecuteInTransaction(StateManager,
                                                                       (tran, token, state) => queue.TryPeekAsync(tran, TimeSpan.FromSeconds(4), token), cancellationToken : cancellationToken)
                                                 .ConfigureAwait(false);

                    if (!taskDescriptionMessage.HasValue)
                    {
                        continue;
                    }

                    //deserialize task description, create task implementation
                    var description    = taskDescriptionMessage.Value.CreateMessage <TaskDescription>();
                    var implementation = TaskDescription.ToTaskImplementation(_typeLocator, description);
                    if (implementation == null)
                    {
                        ServiceEventSourceMessageCallback?
                        .Invoke($"Received TaskDescription message with type {description.TaskType} and id {description.TaskId} that could not be transformed into a TaskImplementation type.");

                        continue;
                    }

                    //run task
                    ServiceEventSourceMessageCallback?
                    .Invoke($"Processing TaskDescription message with type {description.TaskType} and id {description.TaskId}, using implementation {implementation.GetType().Name}.");

                    await implementation.ExecuteAsync().ConfigureAwait(false);

                    var taskDescriptionMessageDequeued = await TimeoutRetryHelper
                                                         .ExecuteInTransaction(StateManager,
                                                                               (tran, token, state) => queue.TryDequeueAsync(tran, TimeSpan.FromSeconds(4), token), cancellationToken : cancellationToken)
                                                         .ConfigureAwait(false);

                    //dequeue task, compare it with the peeked task
                    if (taskDescriptionMessageDequeued.HasValue &&
                        taskDescriptionMessageDequeued.Value.Payload.GetHashCode()
                        == taskDescriptionMessage.Value.Payload.GetHashCode())
                    {
                        ServiceEventSourceMessageCallback?
                        .Invoke($"Processed TaskDescription message with type {description.TaskType} and id {description.TaskId}, using implementation {implementation.GetType().Name}.");
                    }
                    else
                    {
                        ServiceEventSourceMessageCallback?
                        .Invoke($"Unexpected error. The peeked message is not the dequeued message.");
                    }
                }
                catch (TaskCanceledException)
                {//swallow and move on..
                }
                catch (OperationCanceledException)
                {//swallow and move on..
                }
                catch (ObjectDisposedException)
                {//swallow and move on..
                }
                catch (Exception ex)
                {
                    ServiceEventSourceMessageCallback?.Invoke($"Exception caught while processing messages:'{ex.Message}'");
                    //swallow and move on..
                }
                finally
                {
                    await Task.Delay(Period, cancellationToken).ConfigureAwait(false);
                }
            }
        }
Exemple #11
0
 public async Task <long> GetQueueCount(CancellationToken cancellationToken)
 {
     return(await TimeoutRetryHelper.ExecuteInTransaction(_stateManager, (tx, token, state) => Queue.GetCountAsync(tx), cancellationToken : cancellationToken));
 }