/// <summary> /// Invoke the receive side of the default /// pipeline with the passed message /// </summary> /// <param name="provider"></param> /// <param name="id"></param> /// <param name="message"></param> /// <returns></returns> private static async Task Invoke( IServiceProvider provider, string id, object message ) { var context = new FakeTransactionContext(); var msg = new Message( new Dictionary <string, string>() { { Headers.MessageId, id } }, message ); var serializer = provider.GetService <ISerializer>(); var invoker = provider.GetService <IPipelineInvoker>(); AmbientTransactionContext.SetCurrent(context); var transportMessage = await serializer.Serialize(msg); var ctx = new IncomingStepContext(transportMessage, context); await invoker.Invoke(ctx); context.Dispose(); }
void DoWork() { while (keepWorking) { try { using (var tx = new TransactionScope()) { var transactionContext = new AmbientTransactionContext(); var message = queue.ReceiveMessage(transactionContext); if (message == null) { Thread.Sleep(200); continue; } var destinationForThisMessage = getNextDestination(); log.Debug("Received message {0} will be forwarded to {1}", message.Id, destinationForThisMessage); queue.Send(destinationForThisMessage, message.ToForwardableMessage(), transactionContext); tx.Complete(); } } catch (Exception exception) { log.Error("An error occurred while trying to process a message: {0}", exception); } } }
public void CanSendMessagesDirectlyToExchange(int mode) { // arrange var routeUsingExchanges = mode > 1; var exchangeAsAddress = mode > 2; var sender = GetQueue("test.sender", removeExiting: true, oneExchangePerType: routeUsingExchanges, inputExchange: exchangeAsAddress ? "ex-test.sender" : null); var recipient = GetQueue("test.recipient", removeExiting: true, oneExchangePerType: true, inputExchange: "ex-test.recipient"); // act using (var tx = new TransactionScope()) { var ctx = new AmbientTransactionContext(); var msg = new TransportMessageToSend { Body = Encoding.GetBytes("this is a message!") }; sender.Send("@ex-test.recipient", msg, ctx); sender.Send("@ex-test.recipient", msg, ctx); sender.Send("@ex-test.recipient", msg, ctx); tx.Complete(); } // assert var receivedTransportMessages = GetAllMessages(recipient); receivedTransportMessages.Count.ShouldBe(3); }
public void CanSendMessagesInTransaction(bool commitTransactionAndExpectMessagesToBeThere) { // arrange var sender = GetQueue("test.tx.sender"); var recipient = GetQueue("test.tx.recipient"); // act using (var tx = new TransactionScope()) { var ctx = new AmbientTransactionContext(); var msg = new TransportMessageToSend { Body = Encoding.GetBytes("this is a message!") }; sender.Send(recipient.InputQueue, msg, ctx); sender.Send(recipient.InputQueue, msg, ctx); sender.Send(recipient.InputQueue, msg, ctx); if (commitTransactionAndExpectMessagesToBeThere) { tx.Complete(); } } // assert var receivedTransportMessages = GetAllMessages(recipient); receivedTransportMessages.Count.ShouldBe(commitTransactionAndExpectMessagesToBeThere ? 3 : 0); }
async Task ProcessMessage(TransactionContext context, TransportMessage transportMessage) { try { AmbientTransactionContext.SetCurrent(context); var stepContext = new IncomingStepContext(transportMessage, context); await _pipelineInvoker.Invoke(stepContext); try { await context.Complete(); } catch (Exception exception) { _log.Error(exception, "An error occurred when attempting to complete the transaction context"); } } catch (OperationCanceledException exception) { context.Abort(); _log.Error(exception, "Worker was aborted while handling message {messageLabel}", transportMessage.GetMessageLabel()); } catch (Exception exception) { context.Abort(); _log.Error(exception, "Unhandled exception while handling message {messageLabel}", transportMessage.GetMessageLabel()); } finally { AmbientTransactionContext.SetCurrent(null); } }
public void MessageIsSentWhenAmbientTransactionIsCommitted() { using (var tx = new TransactionScope()) { var ctx = new AmbientTransactionContext(); senderQueue.Send(DestinationQueueName, serializer.Serialize(new Message { Messages = new object[] { "W00t!" }, }), ctx); tx.Complete(); } var msmqMessage = Receive(); Assert.IsNotNull(msmqMessage, "No message was received within timeout!"); var transportMessage = (ReceivedTransportMessage)msmqMessage.Body; var message = serializer.Deserialize(transportMessage); message.Messages[0].ShouldBe("W00t!"); }
void DoWork(IReceiveMessages messageQueue) { try { using (var tx = new TransactionScope()) { var ctx = new AmbientTransactionContext(); var receivedTransportMessage = messageQueue.ReceiveMessage(ctx); if (receivedTransportMessage == null) { return; } try { SendMessage(receivedTransportMessage); tx.Complete(); } catch (Exception e) { log.Error(e, "Could not send message {0} to destination URI {1} - waiting 1 sec before retrying", receivedTransportMessage.Id, destinationUri); Thread.Sleep(TimeSpan.FromSeconds(1)); } } } catch (Exception e) { log.Error("Unhandled exception during receive operation", e); } }
public void MessageIsNotSentWhenAmbientTransactionIsNotCommitted() { using (new TransactionScope()) { var ctx = new AmbientTransactionContext(); senderQueue.Send(DestinationQueueName, serializer.Serialize(new Message { Messages = new object[] { "W00t! should not be delivered!" } }), ctx); //< we exit the scope without completing it! } var transportMessage = Receive(); if (transportMessage != null) { Assert.Fail("No messages should have been received! ARGGH: {0}", transportMessage.Body); } }
async Task TryProcessNextMessage() { var parallelOperation = _parallelOperationsManager.TryBegin(); if (!parallelOperation.CanContinue()) { await _backoffStrategy.WaitAsync(_cancellationToken); return; } try { using (parallelOperation) using (var context = new TransactionContextWithOwningBus(_owningBus)) { var transportMessage = await ReceiveTransportMessage(_cancellationToken, context); if (transportMessage == null) { context.Dispose(); // get out quickly if we're shutting down if (_cancellationToken.IsCancellationRequested || _busDisposalCancellationToken.IsCancellationRequested) { return; } // no need for another thread to rush in and discover that there is no message //parallelOperation.Dispose(); await _backoffStrategy.WaitNoMessageAsync(_cancellationToken); return; } _backoffStrategy.Reset(); try { AmbientTransactionContext.SetCurrent(context); await ProcessMessage(context, transportMessage); } finally { AmbientTransactionContext.SetCurrent(null); } } } catch (OperationCanceledException) when(_cancellationToken.IsCancellationRequested || _busDisposalCancellationToken.IsCancellationRequested) { // we're shutting down } catch (Exception exception) { _log.Error(exception, "Unhandled exception in worker {workerName}", Name); } }
public void CanReceiveAndDoOneSingleSendAtomically(bool commitTransactionAndExpectMessagesToBeThere) { sender.Send(receiver.InputQueueAddress, MessageWith("hello"), new NoTransaction()); var destination1 = factory.CreateReceiver("destination1"); Thread.Sleep(300.Milliseconds()); // pretend that this is a message handler tx scope... using (var tx = new TransactionScope()) { var ctx = new AmbientTransactionContext(); // arrange var receivedTransportMessage = receiver.ReceiveMessage(ctx); Assert.That(receivedTransportMessage, Is.Not.Null); // act sender.Send(destination1.InputQueueAddress, MessageWith("hello mr. 1"), ctx); if (commitTransactionAndExpectMessagesToBeThere) { tx.Complete(); } } Thread.Sleep(300.Milliseconds()); // assert var msg1 = destination1.ReceiveMessage(new NoTransaction()); if (commitTransactionAndExpectMessagesToBeThere) { Assert.That(msg1, Is.Not.Null); Assert.That(Encoding.GetString(msg1.Body), Is.EqualTo("hello mr. 1")); using (new TransactionScope()) { var ctx = new AmbientTransactionContext(); var receivedTransportMessage = receiver.ReceiveMessage(ctx); Assert.That(receivedTransportMessage, Is.Null); } } else { Assert.That(msg1, Is.Null); using (new TransactionScope()) { var ctx = new AmbientTransactionContext(); var receivedTransportMessage = receiver.ReceiveMessage(ctx); Assert.That(receivedTransportMessage, Is.Not.Null); Assert.That(Encoding.GetString(receivedTransportMessage.Body), Is.EqualTo("hello")); } } }
public void MultipleSendOperationsToMultipleQueuesAreEnlistedInTheSameTransaction(bool commitTransactionAndExpectMessagesToBeThere) { // arrange const string queueName1 = "test.tx.queue1"; const string queueName2 = "test.tx.queue2"; var recipient1 = NewRawMsmqQueue(queueName1); var recipient2 = NewRawMsmqQueue(queueName2); disposables.Add(recipient1); disposables.Add(recipient2); var encoding = Encoding.UTF8; using (var tx = new TransactionScope()) { var ctx = new AmbientTransactionContext(); // act senderQueue.Send(queueName1, new TransportMessageToSend { Body = encoding.GetBytes("yo dawg 1!") }, ctx); senderQueue.Send(queueName1, new TransportMessageToSend { Body = encoding.GetBytes("yo dawg 2!") }, ctx); senderQueue.Send(queueName2, new TransportMessageToSend { Body = encoding.GetBytes("yo dawg 3!") }, ctx); senderQueue.Send(queueName2, new TransportMessageToSend { Body = encoding.GetBytes("yo dawg 4!") }, ctx); if (commitTransactionAndExpectMessagesToBeThere) { tx.Complete(); } } // assert var allMessages = GetAllMessages(recipient1).Concat(GetAllMessages(recipient2)).ToList(); if (commitTransactionAndExpectMessagesToBeThere) { allMessages.Count.ShouldBe(4); var receivedMessages = allMessages.Select(m => encoding.GetString(m.Body)).ToList(); receivedMessages.ShouldContain("yo dawg 1!"); receivedMessages.ShouldContain("yo dawg 2!"); receivedMessages.ShouldContain("yo dawg 3!"); receivedMessages.ShouldContain("yo dawg 4!"); } else { allMessages.Count.ShouldBe(0); } }
public void CleanupIsPerformedEvenIfTransactionIsNotCommitted() { int cleanupCalled = 0; var tx = new TransactionScope(); var ctx = new AmbientTransactionContext(); ctx.Cleanup += () => { cleanupCalled++; }; tx.Dispose(); cleanupCalled.ShouldBe(1); }
public void CleanupIsPerformedOnDispose() { bool cleanupCalled = false; var tx = new TransactionScope(); var ctx = new AmbientTransactionContext(); ctx.Cleanup += () => { cleanupCalled = true; }; tx.Dispose(); cleanupCalled.ShouldBe(true); }
public void CleanupIsPerformedOnce() { int cleanupCalled = 0; var tx = new TransactionScope(); var ctx = new AmbientTransactionContext(); ctx.Cleanup += () => { cleanupCalled++; }; tx.Complete(); cleanupCalled.ShouldBe(0); tx.Dispose(); cleanupCalled.ShouldBe(1); }
/// <summary> /// Extension method on <see cref="IBus"/> that allows for asynchronously publishing a request and dispatching /// the received reply to the continuation. /// </summary> /// <typeparam name="TReply">Specifies the expected type of the reply. Can be any type compatible with the actually received reply</typeparam> /// <param name="bus">The bus instance to use to publish the request</param> /// <param name="request">The request message</param> /// <param name="optionalHeaders">Headers to be included in the request message</param> /// <param name="timeout">Optionally specifies the max time to wait for a reply. If this time is exceeded, a <see cref="TimeoutException"/> is thrown</param> /// <param name="externalCancellationToken">An external cancellation token from some outer context that cancels waiting for a reply</param> /// <returns></returns> public static async Task <TReply> PublishRequest <TReply>(this IBus bus, object request, IDictionary <string, string> optionalHeaders = null, TimeSpan?timeout = null, CancellationToken externalCancellationToken = default) { var currentTransactionContext = AmbientTransactionContext.Current; try { AmbientTransactionContext.SetCurrent(null); return(await InnerProcessRequest <TReply>((eventMessage, headers) => bus.Publish(eventMessage, headers), request, optionalHeaders, timeout, externalCancellationToken)); } finally { AmbientTransactionContext.SetCurrent(currentTransactionContext); } }
/// <summary> /// Extension method on <see cref="IBus"/> that allows for asynchronously sending a request and dispatching /// the received reply to the continuation. /// </summary> /// <typeparam name="TReply">Specifies the expected type of the reply. Can be any type compatible with the actually received reply</typeparam> /// <param name="bus">The bus instance to use to send the request</param> /// <param name="request">The request message</param> /// <param name="optionalHeaders">Headers to be included in the request message</param> /// <param name="timeout">Optionally specifies the max time to wait for a reply. If this time is exceeded, a <see cref="TimeoutException"/> is thrown</param> /// <returns></returns> public static async Task <TReply> SendRequest <TReply>(this IBus bus, object request, Dictionary <string, string> optionalHeaders = null, TimeSpan?timeout = null) { var currentTransactionContext = AmbientTransactionContext.Current; try { AmbientTransactionContext.SetCurrent(null); return(await InnerSendRequest <TReply>(bus, request, optionalHeaders, timeout)); } finally { AmbientTransactionContext.SetCurrent(currentTransactionContext); } }
public void DoesNotBlockOnCompletingTransactionContext() { var bus = _activator.Bus.Advanced.SyncBus; var gotMessage = new ManualResetEvent(false); _activator.Handle <string>(async str => gotMessage.Set()); using (var aspNet = new AspNetSimulatorSynchronizationContext()) { aspNet.Post(s => { using (var context = new DefaultSyncTransactionContextScope()) { var transactionContext = AmbientTransactionContext.Current; try { // enlist some other async thing transactionContext.OnCommitted(async() => { Console.WriteLine("waiting...."); await Task.Delay(100); Console.WriteLine("waiting...."); await Task.Delay(100); Console.WriteLine("waiting...."); await Task.Delay(100); }); // enlist an operation in the context bus.SendLocal("HEJ MED DIG MIN VEN"); context.Complete(); } finally { AmbientTransactionContext.SetCurrent(null); } } }, null); gotMessage.WaitOrDie(TimeSpan.FromSeconds(3)); } }
public void DoesNotLeakConnectionsForEachMessage() { // Low timeout + pool size will quickly show the issue. var cs = ConnectionString + ";Connect Timeout = 5;Max Pool Size=10"; var transport = new SqlServerMessageQueue(cs, TableName, InputQueueName); transport.EnsureTableIsCreated(); transport.PurgeInputQueue(); for (int i = 0; i < 100; i++) { using (var tx = new TransactionScope()) { var ctx = new AmbientTransactionContext(); transport.Send(InputQueueName, new TransportMessageToSend() { Body = new byte[0], Headers = new Dictionary <string, object>(), Label = "test" }, ctx); tx.Complete(); } } }
async Task ProcessMessage(TransactionContext context, TransportMessage transportMessage) { try { context.Items["OwningBus"] = _owningBus; AmbientTransactionContext.SetCurrent(context); var incomingSteps = _pipeline.ReceivePipeline(); var stepContext = new IncomingStepContext(transportMessage, context); await _pipelineInvoker.Invoke(stepContext, incomingSteps); try { await context.Complete(); } catch (Exception exception) { _log.Error(exception, "An error occurred when attempting to complete the transaction context"); } } catch (ThreadAbortException exception) { context.Abort(); _log.Error(exception, $"Worker was killed while handling message {transportMessage.GetMessageLabel()}"); } catch (Exception exception) { context.Abort(); _log.Error(exception, $"Unhandled exception while handling message {transportMessage.GetMessageLabel()}"); } finally { AmbientTransactionContext.SetCurrent(null); } }
public void CanSendMessagesDirectlyToExchangeWithRoutingKey(int mode) { // arrange var routeUsingExchanges = mode > 1; var exchangeAsAddress = mode > 2; var sender = GetQueue("test.sender", removeExiting: true, oneExchangePerType: routeUsingExchanges, inputExchange: exchangeAsAddress ? "ex-test.sender" : null); var recipient = GetQueue("test.recipient", removeExiting: true); using (var cm = new ConnectionManager(ConnectionString, "test.sender")) using (var conn = cm.GetConnection()) using (var model = conn.CreateModel()) { model.QueueBind("test.recipient", "ex-test.recipient", "sample"); } // act using (var tx = new TransactionScope()) { var ctx = new AmbientTransactionContext(); var msg = new TransportMessageToSend { Body = Encoding.GetBytes("this is a message!") }; sender.Send("*****@*****.**", msg, ctx); sender.Send("*****@*****.**", msg, ctx); sender.Send("*****@*****.**", msg, ctx); tx.Complete(); } // assert var receivedTransportMessages = GetAllMessages(recipient); receivedTransportMessages.Count.ShouldBe(3); }
protected override void TearDown() { AmbientTransactionContext.SetCurrent(null); _activator.Dispose(); }
static void Run(Parameters parameters) { if (string.IsNullOrWhiteSpace(parameters.ErrorQueueName)) { throw new NiceException("Please specify the name of an error queue"); } if (string.IsNullOrWhiteSpace(parameters.Host)) { throw new NiceException("Please specify the hostname"); } using (var rabbitMqMessageQueue = new RabbitMqMessageQueue(parameters.Host, parameters.ErrorQueueName)) { using (var tx = new TransactionScope()) { var transactionContext = new AmbientTransactionContext(); var allTheMessages = GetAllTheMessages(rabbitMqMessageQueue, transactionContext); foreach (var message in allTheMessages) { var transportMessageToSend = message.ToForwardableMessage(); try { if (!transportMessageToSend.Headers.ContainsKey(Headers.SourceQueue)) { throw new NiceException("Message {0} does not have a source queue header - it will be moved back to the input queue", message.Id); } var sourceQueue = (string)transportMessageToSend.Headers[Headers.SourceQueue]; if (parameters.AutoMoveAllMessages.GetValueOrDefault()) { rabbitMqMessageQueue.Send(sourceQueue, transportMessageToSend, transactionContext); Print("Moved {0} to {1}", message.Id, sourceQueue); } else { var answer = PromptChar(new[] { 'y', 'n' }, "Would you like to move {0} to {1}? (y/n)", message.Id, sourceQueue); if (answer == 'y') { rabbitMqMessageQueue.Send(sourceQueue, transportMessageToSend, transactionContext); Print("Moved {0} to {1}", message.Id, sourceQueue); } else { rabbitMqMessageQueue.Send(rabbitMqMessageQueue.InputQueueAddress, transportMessageToSend, transactionContext); Print("Moved {0} to {1}", message.Id, rabbitMqMessageQueue.InputQueueAddress); } } } catch (NiceException e) { Print(e.Message); rabbitMqMessageQueue.Send(rabbitMqMessageQueue.InputQueueAddress, transportMessageToSend, transactionContext); } } if (parameters.DryRun.GetValueOrDefault()) { Print("Aborting queue transaction"); return; } if (!parameters.Interactive) { tx.Complete(); return; } var commitAnswer = PromptChar(new[] { 'y', 'n' }, "Would you like to commit the queue transaction?"); if (commitAnswer == 'y') { Print("Committing queue transaction"); tx.Complete(); return; } Print("Queue transaction aborted"); } } }
public void CanSendAndReceiveMessages(int count, int consumers) { const string senderInputQueue = "test.rabbit.sender"; const string receiverInputQueue = "test.rabbit.receiver"; var sender = GetQueue(senderInputQueue); var receiver = GetQueue(receiverInputQueue); var totalMessageCount = count * consumers; var stopwatch = Stopwatch.StartNew(); Console.WriteLine("Sending {0} messages", totalMessageCount); Enumerable.Range(0, totalMessageCount).ToList() .ForEach( i => sender.Send(receiverInputQueue, new TransportMessageToSend { Body = Encoding.UTF7.GetBytes("w00t! message " + i) }, new NoTransaction())); var totalSeconds = stopwatch.Elapsed.TotalSeconds; Console.WriteLine("Sending {0} messages took {1:0.0} s - that's {2:0} msg/s", totalMessageCount, totalSeconds, totalMessageCount / totalSeconds); Thread.Sleep(1.Seconds()); Console.WriteLine("Receiving {0} messages", totalMessageCount); long receivedMessageCount = 0; stopwatch = Stopwatch.StartNew(); var threads = Enumerable.Range(0, consumers) .Select(i => new Thread(_ => { var gotNoMessageCount = 0; do { using (var scope = new TransactionScope()) { var ctx = new AmbientTransactionContext(); var receivedTransportMessage = receiver.ReceiveMessage(ctx); if (receivedTransportMessage == null) { gotNoMessageCount++; continue; } Encoding.UTF7.GetString(receivedTransportMessage.Body).ShouldStartWith("w00t! message "); Interlocked.Increment(ref receivedMessageCount); scope.Complete(); } } while (gotNoMessageCount < 3); })) .ToList(); threads.ForEach(t => t.Start()); threads.ForEach(t => t.Join()); totalSeconds = stopwatch.Elapsed.TotalSeconds; Console.WriteLine("Receiving {0} messages spread across {1} consumers took {2:0.0} s - that's {3:0} msg/s", totalMessageCount, consumers, totalSeconds, totalMessageCount / totalSeconds); receivedMessageCount.ShouldBe(totalMessageCount); }