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();
                }
            }
        }
예제 #2
0
        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());
                }
        }
예제 #3
0
        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
            }
        }
예제 #4
0
        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);
        }
예제 #5
0
        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
            }
        }
예제 #6
0
 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);
             }
         }
 }
예제 #7
0
        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
            }
        }
예제 #10
0
        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
            }
        }
예제 #11
0
        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();
                }
            }
        }
예제 #12
0
        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);
            }
        }