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); } }
/// <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(); }
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); } }
/// <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)); } }
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); } }
protected override void TearDown() { AmbientTransactionContext.SetCurrent(null); _activator.Dispose(); }