/// <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(); } }