/// <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>
        private async Task UnregisterSubscriberAsync(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);
            var deadLetterQueueName = CreateDeadLetterQueueName(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);
                await StateManager.RemoveAsync(tx, deadLetterQueueName);

                ServiceEventSourceMessage($"Unregistered subscriber: {reference.Name}");
                _queues.TryRemove(queueName, out reference);
            });
        }
        /// <summary>
        /// Registers 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>
        private async Task RegisterSubscriberAsync(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);
                var deadLetterQueueName = CreateDeadLetterQueueName(reference, messageTypeName);

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

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

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

                await StateManager.GetOrAddAsync <IReliableQueue <MessageWrapper> >(tx, queueName);
                await StateManager.GetOrAddAsync <IReliableQueue <MessageWrapper> >(tx, deadLetterQueueName);

                _queues.AddOrUpdate(queueName, reference, (key, old) => reference);
                ServiceEventSourceMessage($"Registered subscriber: {reference.Name}");
            }, cancellationToken : CancellationToken.None);
        }
        /// <summary>
        /// Sends out queued messages for the provided queue.
        /// </summary>
        /// <param name="cancellationToken"></param>
        /// <param name="subscriber"></param>
        /// <param name="queueName"></param>
        /// <returns></returns>
        private async Task ProcessQueues(CancellationToken cancellationToken, ReferenceWrapper subscriber, string queueName)
        {
            var queue = await TimeoutRetryHelper.Execute((token, state) => StateManager.GetOrAddAsync <IReliableQueue <MessageWrapper> >(queueName), cancellationToken : cancellationToken);

            long messageCount = await TimeoutRetryHelper.ExecuteInTransaction(StateManager, (tx, token, state) => queue.GetCountAsync(tx), cancellationToken : cancellationToken);

            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 message = await queue.TryDequeueAsync(tx);
                    if (message.HasValue)
                    {
                        await subscriber.PublishAsync(message.Value);
                    }
                }, cancellationToken : cancellationToken);
            }
        }
        /// <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);

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

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

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

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

            await TimeoutRetryHelper.ExecuteInTransaction(StateManager, async (tx, token, state) =>
            {
                foreach (var subscriber in subscribers)
                {
                    await EnqueueMessageAsync(message, subscriber, tx);
                }
                ServiceEventSourceMessage($"Published message '{message.MessageType}' to {subscribers.Length} subscribers.");
            });
        }
        /// <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
            {
                _semaphore.Wait(cancellationToken);

                if (_initializer.IsSet)
                {
                    return;
                }
                await TimeoutRetryHelper.ExecuteInTransaction(StateManager, async (tx, token, state) =>
                {
                    _queues.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 subscribers = result.Value.Subscribers.ToList();
                        foreach (var subscriber in subscribers)
                        {
                            _queues.TryAdd(subscriber.QueueName, subscriber.ServiceOrActorReference);
                        }
                    }
                }, cancellationToken : cancellationToken);

                _initializer.Set();
            }
            finally
            {
                _semaphore.Release();
            }
        }