/// <summary> /// Starts a new Rebus transcation, enlisting it to be committed in the COMMIT phase of the ambient .NET transaction. Can only be /// called once /// </summary> public static TransactionScope EnlistRebus(this TransactionScope transactionScope) { if (transactionScope == null) throw new ArgumentNullException(nameof(transactionScope)); if (AmbientTransactionContext.Current != null) { throw new InvalidOperationException("Cannot start a new ambient Rebus transaction because there is already one associated with the current execution context!"); } if (Transaction.Current == null) { throw new InvalidOperationException( "Cannot enlist a new ambient Rebus transaction in the current transaction scope, but there's no current transaction" + " on the thread!! Did you accidentally begin the transaction scope WITHOUT the TransactionScopeAsyncFlowOption.Enabled" + " option? You must ALWAYS remember the TransactionScopeAsyncFlowOption.Enabled switch when you start an ambient .NET" + " transaction and you intend to work with async/await, because otherwise the ambient .NET transaction will not flow" + " properly to threads when executing continuations."); } var transactionContext = new DefaultTransactionContext(); var ambientTransactionBridge = new AmbientTransactionBridge(transactionContext); Transaction.Current.EnlistVolatile(ambientTransactionBridge, EnlistmentOptions.None); Transaction.Current.TransactionCompleted += (o, ea) => { ambientTransactionBridge.Dispose(); }; AmbientTransactionContext.Current = transactionContext; return transactionScope; }
public async Task LotsOfAsyncStuffGoingDown(int numberOfMessages) { var receivedMessages = 0; var messageIds = new ConcurrentDictionary<int, int>(); Console.WriteLine("Sending {0} messages", numberOfMessages); await Task.WhenAll(Enumerable.Range(0, numberOfMessages) .Select(async i => { using (var context = new DefaultTransactionContext()) { await _transport.Send(QueueName, RecognizableMessage(i), context); await context.Complete(); messageIds[i] = 0; } })); Console.WriteLine("Receiving {0} messages", numberOfMessages); using (var timer = new Timer(1000)) { timer.Elapsed += delegate { Console.WriteLine("Received: {0} msgs", receivedMessages); }; timer.Start(); await Task.WhenAll(Enumerable.Range(0, numberOfMessages) .Select(async i => { using (var context = new DefaultTransactionContext()) { var msg = await _transport.Receive(context); await context.Complete(); Interlocked.Increment(ref receivedMessages); var id = int.Parse(msg.Headers["id"]); messageIds.AddOrUpdate(id, 1, (_, existing) => existing + 1); } })); await Task.Delay(1000); } Assert.That(messageIds.Keys.OrderBy(k => k).ToArray(), Is.EqualTo(Enumerable.Range(0, numberOfMessages).ToArray())); var kvpsDifferentThanOne = messageIds.Where(kvp => kvp.Value != 1).ToList(); if (kvpsDifferentThanOne.Any()) { Assert.Fail(@"Oh no! the following IDs were not received exactly once: {0}", string.Join(Environment.NewLine, kvpsDifferentThanOne.Select(kvp => string.Format(" {0}: {1}", kvp.Key, kvp.Value)))); } }
async Task TimerElapsed() { using (var result = await _timeoutManager.GetDueMessages()) { foreach (var dueMessage in result) { var transportMessage = dueMessage.ToTransportMessage(); var returnAddress = transportMessage.Headers[Headers.DeferredRecipient]; _log.Debug("Sending due message {0} to {1}", transportMessage.Headers[Headers.MessageId], returnAddress); Console.WriteLine("Sending due message {0} to {1}", transportMessage.Headers[Headers.MessageId], returnAddress); using (var context = new DefaultTransactionContext()) { await _transport.Send(returnAddress, transportMessage, context); await context.Complete(); } dueMessage.MarkAsCompleted(); } } }
public void WorksWithPrefetch(int prefetch, int numberOfMessages) { var activator = Using(new BuiltinHandlerActivator()); var counter = new SharedCounter(numberOfMessages); Using(counter); activator.Handle<string>(async str => { counter.Decrement(); }); Console.WriteLine("Sending {0} messages", numberOfMessages); var transport = GetTransport(); var tasks = Enumerable.Range(0, numberOfMessages) .Select(i => string.Format("THIS IS MESSAGE # {0}", i)) .Select(async msg => { using (var context = new DefaultTransactionContext()) { var headers = DefaultHeaders(); var body = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(msg)); var transportMessage = new TransportMessage(headers, body); await transport.Send(_queueName, transportMessage, context); await context.Complete(); } }) .ToArray(); Task.WhenAll(tasks).Wait(); Console.WriteLine("Receiving {0} messages", numberOfMessages); var stopwatch = Stopwatch.StartNew(); Configure.With(activator) .Transport(t => { t.UseAzureServiceBus(StandardAzureServiceBusTransportFactory.ConnectionString, _queueName, _mode) .EnablePrefetching(prefetch); }) .Options(o => { o.SetNumberOfWorkers(5); o.SetMaxParallelism(10); }) .Start(); counter.WaitForResetEvent(timeoutSeconds: (int)(numberOfMessages * 0.1 + 3)); var elapsedSeconds = stopwatch.Elapsed.TotalSeconds; Console.WriteLine("Receiving {0} messages took {1:0.0} s - that's {2:0.0} msg/s", numberOfMessages, elapsedSeconds, numberOfMessages / elapsedSeconds); }
protected async Task WithContext(Func<ITransactionContext, Task> contextAction, bool completeTransaction = true) { using (var context = new DefaultTransactionContext()) { await contextAction(context); if (completeTransaction) { await context.Complete(); } } }
public async Task DoesNotReceiveSentMessageWhenTransactionIsNotCommitted() { using (var context = new DefaultTransactionContext()) { await _transport.Send(QueueName, RecognizableMessage(), context); //await context.Complete(); } using (var context = new DefaultTransactionContext()) { var transportMessage = await _transport.Receive(context); Assert.That(transportMessage, Is.Null); } }
public async Task ReceivesSentMessageWhenTransactionIsCommitted() { using (var context = new DefaultTransactionContext()) { await _transport.Send(QueueName, RecognizableMessage(), context); await context.Complete(); } using (var context = new DefaultTransactionContext()) { var transportMessage = await _transport.Receive(context); await context.Complete(); AssertMessageIsRecognized(transportMessage); } }
public static async Task<TransportMessage> AwaitReceive(this ITransport transport, double timeoutSeconds = 5) { var stopwatch = Stopwatch.StartNew(); var timeout = TimeSpan.FromSeconds(timeoutSeconds); while (stopwatch.Elapsed < timeout) { TransportMessage receivedTransportMessage; using (var transactionContext = new DefaultTransactionContext()) { receivedTransportMessage = await transport.Receive(transactionContext); await transactionContext.Complete(); } if (receivedTransportMessage != null) return receivedTransportMessage; } throw new AssertionException($"Did not receive transport message from {transport} within {timeout} timeout"); }
public void CanMeasureTimeSpentInSteps() { var stats = new PipelineStepProfilerStats(); var pipeline = new DefaultPipeline() .OnReceive(new Step300()) .OnReceive(new Step100()) .OnReceive(new Step200()); var profiler = new PipelineStepProfiler(pipeline, stats); var receivePipeline = profiler.ReceivePipeline(); var invoker = new DefaultPipelineInvoker(); var transportMessage = new TransportMessage(new Dictionary<string, string>(), new byte[0]); var transactionContext = new DefaultTransactionContext(); var stepContext = new IncomingStepContext(transportMessage, transactionContext); invoker.Invoke(stepContext, receivePipeline).Wait(); var stepStats = stats.GetStats(); Console.WriteLine(string.Join(Environment.NewLine, stepStats)); }
public async Task ItWorks() { var gotMessage = new ManualResetEvent(false); _activator.Handle<string>(async str => { Console.WriteLine("waiting 6 minutes...."); // longer than the longest asb peek lock in the world... //await Task.Delay(TimeSpan.FromSeconds(3)); await Task.Delay(TimeSpan.FromMinutes(6)); Console.WriteLine("done waiting"); gotMessage.Set(); }); await _bus.SendLocal("hej med dig min ven!"); gotMessage.WaitOrDie(TimeSpan.FromMinutes(6.5)); // shut down bus CleanUpDisposables(); // see if queue is empty using (var transactionContext = new DefaultTransactionContext()) { var message = await _transport.Receive(transactionContext); if (message != null) { throw new AssertionException(string.Format("Did not expect to receive a message - got one with ID {0}", message.Headers.GetValue(Headers.MessageId))); } await transactionContext.Complete(); } }
async Task InnerSend(IEnumerable<string> destinationAddresses, Message logicalMessage) { var currentTransactionContext = AmbientTransactionContext.Current; if (currentTransactionContext != null) { await SendUsingTransactionContext(destinationAddresses, logicalMessage, currentTransactionContext); } else { using (var context = new DefaultTransactionContext()) { await SendUsingTransactionContext(destinationAddresses, logicalMessage, context); await context.Complete(); } } }
static List<int> SendMessages(int messageCount) { var transport = new MsmqTransport(QueueName); MsmqUtil.EnsureQueueExists(MsmqUtil.GetPath(QueueName)); var sendIds = new List<int>(); Enumerable.Range(0, messageCount) .Select(id => new SomeMessage { Id = id }) .ToList() .ForEach(msg => { using (var context = new DefaultTransactionContext()) { transport.Send(QueueName, TransportMessageHelpers.FromString(JsonConvert.SerializeObject(msg)), context).Wait(); context.Complete().Wait(); sendIds.Add(msg.Id); } }); return sendIds; }
string Receive() { using (var context = new DefaultTransactionContext()) { var transportMessage = _transport.Receive(context).Result; context.Complete().Wait(); if (transportMessage == null) return null; return Encoding.UTF8.GetString(transportMessage.Body); } }
void Send(string destinationAddress, string message) { Console.WriteLine("Sending to {0}", destinationAddress); using (var transactionContext = new DefaultTransactionContext()) { _transport.Send(destinationAddress, NewMessage(message), transactionContext).Wait(); transactionContext.Complete().Wait(); } }
async void TryReceiveNewMessage() { using (var operation = _parallelOperationsManager.TryBegin()) { // if we didn't get to do our thing, pause the thread a very short while to avoid thrashing too much if (!operation.CanContinue()) { Thread.Sleep(10); return; } using (var transactionContext = new DefaultTransactionContext()) { AmbientTransactionContext.Current = transactionContext; try { var message = await _transport.Receive(transactionContext); if (message == null) { // no message: finish the tx and wait.... await transactionContext.Complete(); await _backoffStrategy.Wait(); return; } // we got a message, so we reset the backoff strategy _backoffStrategy.Reset(); var context = new IncomingStepContext(message, transactionContext); var stagedReceiveSteps = _pipeline.ReceivePipeline(); await _pipelineInvoker.Invoke(context, stagedReceiveSteps); try { await transactionContext.Complete(); } catch (Exception exception) { _log.Error(exception, "An error occurred when attempting to complete the transaction context"); } } catch (Exception exception) { // we should not end up here unless something is off.... _log.Error(exception, "Unhandled exception in thread worker - the pipeline didn't handle its own errors (this is bad)"); Thread.Sleep(100); } finally { AmbientTransactionContext.Current = null; } } } }
async void TryAsyncReceive(CancellationToken token, IDisposable parallelOperation) { try { using (parallelOperation) using (var context = new DefaultTransactionContext()) { var transportMessage = await ReceiveTransportMessage(token, context); if (transportMessage == null) { context.Dispose(); // no need for another thread to rush in and discover that there is no message //parallelOperation.Dispose(); _backoffStrategy.WaitNoMessage(); return; } _backoffStrategy.Reset(); await ProcessMessage(context, transportMessage); } } catch (TaskCanceledException) { // it's fine - just a sign that we are shutting down } catch (OperationCanceledException) { // it's fine - just a sign that we are shutting down } catch (Exception exception) { _log.Error(exception, "Unhandled exception in thread pool worker"); } }
public void WorksWithPrefetch(int prefetch, int numberOfMessages) { AdjustLogging(LogLevel.Info); var activator = new BuiltinHandlerActivator(); var receivedMessages = 0; var done = new ManualResetEvent(false); activator.Handle<string>(async str => { Interlocked.Increment(ref receivedMessages); if (receivedMessages == numberOfMessages) { done.Set(); } }); Console.WriteLine("Sending {0} messages", numberOfMessages); using (var transport = GetTransport()) { var tasks = Enumerable.Range(0, numberOfMessages) .Select(i => string.Format("THIS IS MESSAGE # {0}", i)) .Select(async msg => { using (var context = new DefaultTransactionContext()) { var headers = DefaultHeaders(); var body = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(msg)); var transportMessage = new TransportMessage(headers, body); await transport.Send(QueueName, transportMessage, context); await context.Complete(); } }) .ToArray(); Task.WhenAll(tasks).Wait(); } Console.WriteLine("Receiving {0} messages", numberOfMessages); var stopwatch = Stopwatch.StartNew(); using (Configure.With(activator) .Transport(t => { t.UseAzureServiceBus(AzureServiceBusTransportFactory.ConnectionString, QueueName) .EnablePrefetching(prefetch); }) .Options(o => { o.SetNumberOfWorkers(5); o.SetMaxParallelism(10); }) .Start()) { done.WaitOrDie(TimeSpan.FromSeconds(numberOfMessages * 0.1 + 3)); } var elapsedSeconds = stopwatch.Elapsed.TotalSeconds; Console.WriteLine("Receiving {0} messages took {1:0.0} s - that's {2:0.0} msg/s", numberOfMessages, elapsedSeconds, numberOfMessages / elapsedSeconds); }
async Task<ReceiveResult> TryReceiveTransportMessage(DefaultTransactionContext transactionContext, CancellationToken cToken) { try { var message = await _transport.Receive(transactionContext, cToken); return new ReceiveResult(message); } //catch (TaskCanceledException tex) //{ // return new ReceiveResult(tex, true); //} catch (Exception exception) { return new ReceiveResult(exception); } }
async void TryReceiveNewMessage() { using (var operation = _parallelOperationsManager.TryBegin()) { // if we didn't get to do our thing, let the OS decide what to do next.... we don't hog the processor if (!operation.CanContinue()) { Thread.Yield(); return; } using (var transactionContext = new DefaultTransactionContext()) { transactionContext.Items["CancellationToken"] = _cancellationTokenSource.Token; AmbientTransactionContext.Current = transactionContext; try { var result = await TryReceiveTransportMessage(transactionContext, _cancellationTokenSource.Token); if (result.Exception != null) { if (result.Exception is TaskCanceledException || result.Exception is OperationCanceledException) { // this is normal - we're being shut down so we just return quickly transactionContext.Dispose(); return; } _log.Warn("An error occurred when attempting to receive transport message: {0}", result.Exception); // error: finish the tx and wait.... transactionContext.Dispose(); await _backoffStrategy.WaitError(); return; } var message = result.TransportMessage; if (message == null) { // no message: finish the tx and wait.... await transactionContext.Complete(); transactionContext.Dispose(); await _backoffStrategy.Wait(); return; } // we got a message, so we reset the backoff strategy _backoffStrategy.Reset(); var context = new IncomingStepContext(message, transactionContext); var stagedReceiveSteps = _pipeline.ReceivePipeline(); await _pipelineInvoker.Invoke(context, stagedReceiveSteps); try { await transactionContext.Complete(); } catch (Exception exception) { _log.Error(exception, "An error occurred when attempting to complete the transaction context"); } } catch (Exception exception) { // we should not end up here unless something is off.... _log.Error(exception, "Unhandled exception in thread worker - the pipeline didn't handle its own errors (this is bad)"); Thread.Sleep(100); } finally { AmbientTransactionContext.Current = null; } } } }
async Task DoWork() { using (var op = _parallelOperationsManager.TryBegin()) { if (!op.CanContinue()) return; using (var transactionContext = new DefaultTransactionContext()) { AmbientTransactionContext.Current = transactionContext; try { var message = await _transport.Receive(transactionContext); if (message == null) { // finish the tx and wait.... await transactionContext.Complete(); await _backoffHelper.Wait(); return; } _backoffHelper.Reset(); var context = new IncomingStepContext(message, transactionContext); transactionContext.Items[StepContext.StepContextKey] = context; var stagedReceiveSteps = _pipeline.ReceivePipeline(); await _pipelineInvoker.Invoke(context, stagedReceiveSteps); await transactionContext.Complete(); } catch (Exception exception) { _log.Error(exception, "Unhandled exception in task worker"); } finally { AmbientTransactionContext.Current = null; } } } }
async Task ProcessMessage(DefaultTransactionContext context, TransportMessage transportMessage) { try { context.Items["OwningBus"] = _owningBus; AmbientTransactionContext.Current = 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.Current = null; } }
public void Run() { Text.PrintLine("Will start receiving messages from '{0}'", InputQueue); if (DefaultOutputQueue != null) { Text.PrintLine("(will provide '{0}' as the default queue to forward messages to)", DefaultOutputQueue); } Text.PrintLine(); while (true) { using (var transactionContext = new DefaultTransactionContext()) { var transportMessage = _transport.Receive(transactionContext, new CancellationTokenSource().Token).Result; if (transportMessage == null) break; try { HandleMessage(transportMessage, transactionContext); transactionContext.Complete().Wait(); } catch (Exception exception) { Text.PrintLine("Failed: {0}", exception.Message); } } Text.PrintLine("No more messages"); } }
public async void DoesntIgnoreDefinedTimeoutWhenReceiving(AzureServiceBusMode mode, int operationTimeoutInSeconds) { var operationTimeout = TimeSpan.FromSeconds(operationTimeoutInSeconds); var connString = StandardAzureServiceBusTransportFactory.ConnectionString; var builder = new ServiceBusConnectionStringBuilder(connString) { OperationTimeout = operationTimeout }; var newConnString = builder.ToString(); var consoleLoggerFactory = new ConsoleLoggerFactory(false); var transport = new AzureServiceBusTransport(newConnString, QueueName, consoleLoggerFactory, new TplAsyncTaskFactory(consoleLoggerFactory), new BusLifetimeEvents()); Using(transport); transport.PurgeInputQueue(); //Create the queue for the receiver since it cannot create it self beacuse of lacking rights on the namespace transport.CreateQueue(QueueName); var senderActivator = new BuiltinHandlerActivator(); var senderBus = Configure.With(senderActivator) .Transport(t => t.UseAzureServiceBus(newConnString, "sender", mode)) .Start(); Using(senderBus); // queue 3 messages await senderBus.Advanced.Routing.Send(QueueName, "message to receiver"); await senderBus.Advanced.Routing.Send(QueueName, "message to receiver2"); await senderBus.Advanced.Routing.Send(QueueName, "message to receiver3"); await Task.Delay(TimeSpan.FromSeconds(2)); // wait a bit to make sure the messages are queued. // receive 1 using (var transactionContext = new DefaultTransactionContext()) { var sw = System.Diagnostics.Stopwatch.StartNew(); var msg = await transport.Receive(transactionContext); sw.Stop(); await transactionContext.Complete(); msg.Should().NotBeNull(); sw.Elapsed.Should().BeLessThan(TimeSpan.FromMilliseconds(1500)); } // receive 2 using (var transactionContext = new DefaultTransactionContext()) { var sw = System.Diagnostics.Stopwatch.StartNew(); var msg = await transport.Receive(transactionContext); sw.Stop(); await transactionContext.Complete(); msg.Should().NotBeNull(); sw.Elapsed.Should().BeLessThan(TimeSpan.FromMilliseconds(1500)); } // receive 3 using (var transactionContext = new DefaultTransactionContext()) { var sw = System.Diagnostics.Stopwatch.StartNew(); var msg = await transport.Receive(transactionContext); sw.Stop(); await transactionContext.Complete(); msg.Should().NotBeNull(); sw.Elapsed.Should().BeLessThan(TimeSpan.FromMilliseconds(1500)); } // receive 4 - NOTHING using (var transactionContext = new DefaultTransactionContext()) { var sw = System.Diagnostics.Stopwatch.StartNew(); var msg = await transport.Receive(transactionContext); sw.Stop(); await transactionContext.Complete(); msg.Should().BeNull(); sw.Elapsed.Should().BeCloseTo(operationTimeout, 2000); } // put 1 more message await senderBus.Advanced.Routing.Send(QueueName, "message to receiver5"); await Task.Delay(TimeSpan.FromSeconds(2)); // wait a bit to make sure the messages are queued. // receive 5 using (var transactionContext = new DefaultTransactionContext()) { var sw = System.Diagnostics.Stopwatch.StartNew(); var msg = await transport.Receive(transactionContext); sw.Stop(); await transactionContext.Complete(); msg.Should().NotBeNull(); sw.Elapsed.Should().BeLessThan(TimeSpan.FromMilliseconds(1500)); } // receive 6 - NOTHING using (var transactionContext = new DefaultTransactionContext()) { var sw = System.Diagnostics.Stopwatch.StartNew(); var msg = await transport.Receive(transactionContext); sw.Stop(); await transactionContext.Complete(); msg.Should().BeNull(); sw.Elapsed.Should().BeCloseTo(operationTimeout, 2000); } }
async Task<TransportMessage> TryReceiveTransportMessage(DefaultTransactionContext transactionContext) { try { var message = await _transport.Receive(transactionContext); return message; } catch (Exception exception) { _log.Warn("An error occurred when attempting to receive transport message: {0}", exception); return null; } }
public AmbientTransactionBridge(DefaultTransactionContext transactionContext) { _transactionContext = transactionContext; }
async Task<TransportMessage> ReceiveTransportMessage(CancellationToken token, DefaultTransactionContext context) { try { return await _transport.Receive(context, token); } catch (TaskCanceledException) { // it's fine - just a sign that we are shutting down throw; } catch (OperationCanceledException) { // it's fine - just a sign that we are shutting down throw; } catch (Exception exception) { _log.Warn("An error occurred when attempting to receive the next message: {0}", exception); _backoffStrategy.WaitError(); return null; } }