public async Task can_route_multiple_message() { var key = Environment.TickCount; using (var router = new SubQueueRouter(testQueueFormatName, GetSubQueue)) { await router.StartAsync(); try { using (var q = new QueueWriter(testQueueFormatName)) { q.Write(new Message { Label = "my.sq", AppSpecific = key }); q.Write(new Message { Label = "my.sq", AppSpecific = key + 1 }); } using (var sq = new QueueReader(testQueueFormatName + ";sq")) { var got = sq.Read(Properties.AppSpecific, timeout: TimeSpan.FromMilliseconds(500)); Assert.AreEqual(key, got.AppSpecific); got = sq.Read(Properties.AppSpecific, timeout: TimeSpan.FromMilliseconds(500)); Assert.AreEqual(key + 1, got.AppSpecific); } } finally { await router.StopAsync(); } } }
public async Task can_remove_subscription() { using (var requestWriter = new QueueWriter(requestFN)) using (var replyReader = new QueueReader(replyFN)) { var addReq = new Message { AppSpecific = (int)PubSubAction.Add, ResponseQueue = replyFN }; addReq.BodyUTF8("thing.one" + Environment.NewLine + "thing.two"); requestWriter.Write(addReq); var removeReq = new Message { AppSpecific = (int)PubSubAction.Remove, ResponseQueue = replyFN }; removeReq.BodyUTF8("thing.one"); requestWriter.Write(removeReq); var listReq = new Message { AppSpecific = (int)PubSubAction.List, ResponseQueue = replyFN }; requestWriter.Write(listReq); var got = replyReader.Read(timeout: TimeSpan.FromSeconds(3)); Assert.IsNotNull(got); Assert.AreEqual("thing.two", got.BodyUTF8()); } }
async Task RunAsync() { await Task.Yield(); try { for (;;) { var msg = _adminQueue.Read(AdminProperties, TimeSpan.Zero) ?? await _adminQueue.ReadAsync(AdminProperties); var ack = msg.Acknowledgement(); switch (ack) { #pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed case MessageClass.ReachQueue: var tcs = ReachQueueCompletionSource(new Tracking(msg.ResponseQueue, msg.CorrelationId)); Task.Run(() => tcs.TrySetResult(ack)); // set result is synchronous by default, make it async break; case MessageClass.ReachQueueTimeout: case MessageClass.AccessDenied: case MessageClass.BadDestinationQueue: case MessageClass.BadEncryption: case MessageClass.BadSignature: case MessageClass.CouldNotEncrypt: case MessageClass.HopCountExceeded: case MessageClass.NotTransactionalMessage: case MessageClass.NotTransactionalQueue: case MessageClass.Deleted: case MessageClass.QueueDeleted: case MessageClass.QueuePurged: case MessageClass.QueueExceedQuota: var tcs1 = ReachQueueCompletionSource(new Tracking(msg.ResponseQueue, msg.CorrelationId)); Task.Run(() => tcs1.TrySetException(new AcknowledgmentException(msg.ResponseQueue, ack))); // set result is synchronous by default, make it async break; case MessageClass.Received: var tcs2 = ReceiveCompletionSource(new Tracking(msg.ResponseQueue, msg.CorrelationId)); Task.Run(() => tcs2.TrySetResult(ack)); // set result is synchronous by default, make it async break; case MessageClass.ReceiveTimeout: var tcs3 = ReceiveCompletionSource(new Tracking(msg.ResponseQueue, msg.CorrelationId)); Task.Run(() => tcs3.TrySetException(new AcknowledgmentException(msg.ResponseQueue, ack))); // set result is synchronous by default, make it async break; #pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed } } } catch (QueueException ex) when(ex.ErrorCode == ErrorCode.OperationCanceled) { // stopped } catch (ObjectDisposedException) { // stopped } }
private async Task <int> PerformReads(string queueName) { ParmCheck.NotNullOrEmpty(nameof(queueName), queueName); int numRead = 0; var options = new JsonSerializerOptions { WriteIndented = true }; var reader = new QueueReader(_connectionString, queueName); FishObservation observation = await reader.Read <FishObservation>(); while (observation != null) { numRead++; Console.WriteLine(JsonSerializer.Serialize(observation, typeof(FishObservation), options)); observation = await reader.Read <FishObservation>(); } return(numRead); }
async Task RunAsync() { await Task.Yield(); var prefixDot = CachePrefix + "."; try { for (;;) { var msg = _input.Read(Properties.All, TimeSpan.Zero) ?? await _input.ReadAsync(Properties.All); if (msg.Label.StartsWith(prefixDot, StringComparison.OrdinalIgnoreCase)) { switch ((MessageCacheAction)msg.AppSpecific) { case MessageCacheAction.Read: SendLastValue(msg); break; case MessageCacheAction.Remove: Invalidate(msg); break; } } else if (msg.Label.Equals(CachePrefix, StringComparison.OrdinalIgnoreCase)) { switch ((MessageCacheAction)msg.AppSpecific) { case MessageCacheAction.ListKeys: SendKeyList(msg); break; case MessageCacheAction.Clear: ClearCache(); break; } } else { StoreLastValue(msg); } } } catch (ObjectDisposedException) { // Stop was called } catch (QueueException ex) when(ex.ErrorCode == ErrorCode.OperationCanceled) { // Stop was called } }
private void Server(int count) { using (var requestQ = new QueueReader(requestQueueFormatName)) using (var replyQ = new QueueWriter(replyQueueFormatName)) { for (int i = 0; i < count; i++) { var msg = requestQ.Read(Properties.All, TimeSpan.FromSeconds(0.5)); msg.CorrelationId = msg.Id; replyQ.Write(msg); } } }
public async Task can_route_transactional_to_other_queue() { using (var router = new TransactionalRouter(inputQueueFormatName, sender, Route)) { await sender.StartAsync(); try { var rtask = router.StartAsync(); input.Write(new Message { Label = "2", AppSpecific = 1 }, QueueTransaction.Single); var got = outRead2.Read(); Assert.AreEqual("2", got.Label); } finally { await router?.StopAsync(); await sender.StopAsync(); } } }
public async Task can_route_many() { using (var input = new QueueWriter(testQueueFormatName)) using (var out1 = new QueueReader(testQueueFormatName + ";one")) using (var out2 = new QueueReader(testQueueFormatName + ";two")) using (var router = new SubQueueRouter(testQueueFormatName, GetSubQueue)) { out1.Purge(); out2.Purge(); for (int i = 0; i < 1000; i++) { input.Write(new Message { Label = "1", AppSpecific = i }); input.Write(new Message { Label = "2", AppSpecific = i }); } var sw = new Stopwatch(); sw.Start(); var rtask = router.StartAsync(); try { for (int i = 0; i < 1000; i++) { var got = out1.Read(Properties.Label | Properties.AppSpecific); Assert.AreEqual("1", got.Label, "Label"); Assert.AreEqual(i, got.AppSpecific, "AppSpecific"); got = out2.Read(Properties.Label | Properties.AppSpecific); Assert.AreEqual("2", got.Label, "Label"); Assert.AreEqual(i, got.AppSpecific, "AppSpecific"); } sw.Stop(); } finally { await router.StopAsync(); } Console.WriteLine($"Reading 2000 routed messages took {sw.ElapsedMilliseconds:N0} MS"); } }
/// <summary>Report any failure to send to destination queues</summary> async Task AdminTask() { await Task.Yield(); var props = Properties.Class | Properties.DestinationQueue | Properties.Label; try { for (;;) { var msg = _adminReader.Read(props, TimeSpan.Zero) ?? await _adminReader.ReadAsync(props); var ack = msg.Acknowledgement(); switch (ack) { case MessageClass.ReachQueueTimeout: case MessageClass.AccessDenied: case MessageClass.BadDestinationQueue: case MessageClass.BadEncryption: case MessageClass.BadSignature: case MessageClass.CouldNotEncrypt: case MessageClass.HopCountExceeded: case MessageClass.NotTransactionalMessage: case MessageClass.NotTransactionalQueue: case MessageClass.Deleted: case MessageClass.QueueDeleted: case MessageClass.QueuePurged: case MessageClass.QueueExceedQuota: case MessageClass.ReceiveTimeout: Console.Error.WriteLine($"WARNING {ack} sending '{msg.Label}' to {msg.DestinationQueue}"); break; } } } catch (QueueException ex) when(ex.ErrorCode == ErrorCode.OperationCanceled) { // stopped } catch (ObjectDisposedException) { // stopped } }
async Task AdminAsync() { try { for (;;) { const Properties adminProps = Properties.Class | Properties.Id | Properties.Label | Properties.DestinationQueue; var msg = _admin.Read(adminProps, TimeSpan.Zero) ?? await _admin.ReadAsync(adminProps); var ack = msg.Acknowledgement(); switch (ack) { case MessageClass.ReachQueueTimeout: case MessageClass.AccessDenied: case MessageClass.BadDestinationQueue: case MessageClass.BadEncryption: case MessageClass.BadSignature: case MessageClass.CouldNotEncrypt: case MessageClass.HopCountExceeded: case MessageClass.NotTransactionalMessage: case MessageClass.NotTransactionalQueue: case MessageClass.Deleted: case MessageClass.QueueDeleted: case MessageClass.QueuePurged: case MessageClass.QueueExceedQuota: Console.Error.WriteLine($"WARNING: message labelled '{msg.Label}' failed to reach '{msg.DestinationQueue}' because {ack}"); break; } } } catch (ObjectDisposedException) { // Stop was called } catch (QueueException ex) when(ex.ErrorCode == ErrorCode.OperationCanceled) { // Stop was called } }
public async Task can_route_non_transactional_to_other_queue() { using (var router = new NonTransactionalRouter(inputQueueFormatName, sender, msg => msg.Label.Contains("1") ? outSend1 : outSend2)) { await sender.StartAsync(); try { var rtask = router.StartAsync(); input.Write(new Message { Label = "2", AppSpecific = 1 }); var got = outRead2.Read(); Assert.AreEqual("2", got.Label); } finally { await router?.StopAsync(); await sender.StopAsync(); } } }
public async Task can_route_non_transactional_to_deadletter() { using (var router = new NonTransactionalRouter(inputQueueFormatName, sender, msg => null)) { await sender.StartAsync(); try { var rtask = router.StartAsync(); input.Write(new Message { Label = "3", AppSpecific = 1 }); var got = dead.Read(); Assert.AreEqual("3", got.Label); } finally { await router?.StopAsync(); await sender.StopAsync(); } } }
/// <summary>Read messages from the input queue and forward to subscribers</summary> async Task MulticastInputDispatcher() { await Task.Yield(); try { for (;;) { var msg = _inputReader.Read(Properties.All, TimeSpan.Zero) ?? await _inputReader.ReadAsync(Properties.All); // we could avoid the lock by using an immutable collection var subscribers = _subscriptions.GetSubscribers(msg.Label); if (subscribers.Count == 0) { continue; } var fn = string.Join(",", subscribers.Select(s => s.FormatName).Distinct()); // create a multi-element format name var q = _responseQueueCache.Open(fn, QueueAccessMode.Send); msg.AdministrationQueue = _adminQueueFormatName; q.Write(msg); } } catch (QueueException ex) when(ex.ErrorCode == ErrorCode.OperationCanceled) { // stopped } catch (ObjectDisposedException) { // stopped } catch (Exception ex) { Console.Error.WriteLine("ERROR MulticastInputDispatcher: " + ex); } }
/// <summary>Respond to requests to subscribe or unsubscribe</summary> /// <remarks> /// Add Subscription: label: tag, AppSpecific: Add, ResponseQueue: subscriber identity, Body: UTF-8 subjects, one per line /// Set Subscriptions: label: tag, AppSpecific: Set, ResponseQueue: subscriber identity, Body: UTF-8 subjects, one per line /// Remove Subscriptions: label: tag, AppSpecific: Remove, ResponseQueue: subscriber identity, Body: UTF-8 subjects, one per line /// Clear Subscriptions: label: tag, AppSpecific: Clear, ResponseQueue: subscriber identity /// List Subscriptions: label: tag, AppSpecific: List, ResponseQueue: subscriber identity /// </remarks> async Task SubscriptionLoop() { await Task.Yield(); Properties props = Properties.AppSpecific | Properties.Label | Properties.ResponseQueue | Properties.Body; try { for (;;) { var msg = _clientRequestReader.Read(props, TimeSpan.Zero) ?? await _clientRequestReader.ReadAsync(props); var tag = msg.Label; string responseQueue = msg.ResponseQueue; var labels = msg.BodyUTF8()?.Split(new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries); if (responseQueue.Length == 0) { Console.Error.WriteLine("Request with no response queue, ignoring message: " + msg.Id); continue; } PubSubAction action = (PubSubAction)msg.AppSpecific; switch (action) { case PubSubAction.Add: lock (_subscriptions.SyncRoot) { foreach (var l in labels) { _subscriptions.Add(l, responseQueue, tag); } } break; case PubSubAction.Remove: lock (_subscriptions.SyncRoot) { foreach (var l in labels) { _subscriptions.Remove(l, responseQueue, tag); } } break; case PubSubAction.Set: var targetSubs = new HashSet <string>(labels); lock (_subscriptions.SyncRoot) { var currentSubs = _subscriptions.GetSubscriptions(responseQueue, tag); foreach (var l in targetSubs.Where(l => !currentSubs.Contains(l))) { _subscriptions.Add(l, responseQueue, tag); } foreach (var l in currentSubs.Where(l => !targetSubs.Contains(l))) { _subscriptions.Remove(l, responseQueue, tag); } } break; case PubSubAction.Clear: _subscriptions.Clear(responseQueue, tag); break; case PubSubAction.List: HashSet <string> current = _subscriptions.GetSubscriptions(responseQueue, tag); var reply = new Message { AdministrationQueue = _adminQueueFormatName, Label = tag, CorrelationId = msg.Id }; reply.BodyUTF8(string.Join(Environment.NewLine, current.OrderBy(l => l))); var q = _responseQueueCache.Open(responseQueue, QueueAccessMode.Send); q.Write(reply); break; default: Console.Error.WriteLine($"Request with invalid {nameof(msg.AppSpecific)} {action}, ignoring request, message id '{msg.Id}' for '{responseQueue}'"); break; } } } catch (QueueException ex) when(ex.ErrorCode == ErrorCode.OperationCanceled) { // stopped } catch (ObjectDisposedException) { // stopped } catch (Exception ex) { Console.Error.WriteLine("ERROR SubscriptionLoop: " + ex); } }