Automatonymous StateMachine how to handle NotAcceptedStateMachineException - masstransit

Is there any way to handle NotAcceptedStateMachineException in generic way e.g. return to client some user friendly message in request-response approach?
Automatonymous.NotAcceptedStateMachineException: ExampleSagaState(8dc388ea-99b3-4d21-a3e5-915c4185ec12) Saga exception on receipt of IExampleRequest: Not accepted in state ExampleState
---> Automatonymous.UnhandledEventException: The ExampleEvent event is not handled during the ExampleState state for the ExampleStateMachineSaga state machine
at Automatonymous.AutomatonymousStateMachine`1.DefaultUnhandledEventCallback(UnhandledEventContext`1 context)
at Automatonymous.AutomatonymousStateMachine`1.UnhandledEvent(EventContext`1 context, State state)
at Automatonymous.States.StateMachineState`1.Automatonymous.State<TInstance>.Raise[T](EventContext`2 context)
at Automatonymous.AutomatonymousStateMachine`1.Automatonymous.StateMachine<TInstance>.RaiseEvent[T](EventContext`2 context)
at Automatonymous.Pipeline.StateMachineSagaMessageFilter`2.Send(SagaConsumeContext`2 context, IPipe`1 next)
--- End of inner exception stack trace ---
at Automatonymous.Pipeline.StateMachineSagaMessageFilter`2.Send(SagaConsumeContext`2 context, IPipe`1 next)
at Automatonymous.Pipeline.StateMachineSagaMessageFilter`2.Send(SagaConsumeContext`2 context, IPipe`1 next)
at MassTransit.Saga.SendSagaPipe`2.Send(SagaRepositoryContext`2 context)
at MassTransit.Saga.SendSagaPipe`2.Send(SagaRepositoryContext`2 context)
at MassTransit.RedisIntegration.Contexts.RedisSagaRepositoryContextFactory`1.Send[T](ConsumeContext`1 context, IPipe`1 next)
at .MassTransit.RedisIntegration.Contexts.RedisSagaRepositoryContextFactory`1.Send[T](ConsumeContext`1 context, IPipe`1 next)
at MassTransit.Saga.Pipeline.Filters.CorrelatedSagaFilter`2.GreenPipes.IFilter<MassTransit.ConsumeContext<TMessage>>.Send(ConsumeContext`1 context, IPipe`1 next)
I'm using request/response to execute a state machine:
IRequestClient<IExampleRequest> client = this._busControl.CreateRequestClient<IExampleMessage>(address, (MassTransit.RequestTimeout)Timeout);
Response<IExampleResponse> response = await client.GetResponse<TRes>(message, new CancellationToken(), new MassTransit.RequestTimeout()).ConfigureAwait(false);
I'd like to get a response of that TRes type but instead the timeout is shown.
I have tried to handle this by using OnUnhandledEvent but here i'm losing info about response type so i can't send it back.
Any ideas how to solve that? Thanks

Without seeing the state machine code, it's purely a guess, but the error message is clear. The state machine isn't in a state where that event is handled.
I'd suggest reviewing some samples covering state machine, if possible watching the entire season of videos discussing that sample, state machines, and how they work in MassTransit.

Related

Weird Behavior while missing an await keyword generates error: Cannot access a disposed context instance

I recently ran into this error. I have never came across this before so wondering!
Cannot access a disposed context instance. A common cause of this error is disposing a context instance that was resolved from dependency injection and then later trying to use the same context instance elsewhere in your application. This may occur if you are calling 'Dispose' on the context instance, or wrapping it in a using statement. If you are using dependency injection, you should let the dependency injection container take care of disposing context instances.
Object name: 'OrderDbContext'.
The only thing i missed which produced this error is the await keyword in the controller action method before _mediator.Send(checkoutCommand); Once I added the await keyword this error vanished.
What (the heck) is wrong with missing this await keyword? The error does not explicitly state that. Can someone explain why missing an await keyword cause an error that database context is disposed?
Controller Action:
public async Task<IActionResult> Checkout(CheckoutOrderCommand checkoutCommand)
{
**var id = _mediator.Send(checkoutCommand);**
return CreatedAtRoute("Get Order by Id", id);
}
CQRS Mediatr Instructions
public class CheckoutOrderCommandHandler : IRequestHandler<CheckoutOrderCommand, int>
{
private readonly IOrderUow _orderUow;
private readonly IMapper _mapper;
public CheckoutOrderCommandHandler(IOrderUow orderUow,
IMapper mapper)
{
_orderUow = orderUow;
_mapper = mapper;
}
public async Task<int> Handle(CheckoutOrderCommand request, CancellationToken cancellationToken)
{
var order = _mapper.Map<Order>(request);
// Populate base entity fields
order.CreatedBy = Constants.CurrentUser;
// create new order
var newOrder = await _orderUow.AddOrderAsync(order);
return newOrder.Id;
}
}
Unit of work implementation
public class OrderUow : IOrderUow
{
private readonly OrderDbContext _orderContext;
public OrderUow(OrderDbContext orderContext)
{
_orderContext = orderContext;
}
public async Task<Order> AddOrderAsync(Order order)
{
try
{
await _orderContext.Orders.AddAsync(order);
await _orderContext.SaveChangesAsync();
}
catch (Exception ex)
{
}
return order;
}
}
Missing an await without explicitly handling the task that is returned will mean that the code calling the asynchronous method will not create a resumption point and instead will continue executing to completion, in your case leading to the disposal of the DbContext.
Asyncrhronous code is multi-threaded behind the scenes. Think of it this way, your web request enters on Thread #1 which creates a DbContext instance via an IoC container, calls an asynchronous method, then returns. When the code calls an async method, it automatically hands that code off to a worker thread to execute. By adding await you tell .Net to create a resume point to come back to. That may be the original calling thread, or a new worker thread, though the important thing is that the calling code will resume only after the async method completes. Without await, there is no resume point, so the calling code continues after the async method call. This can lead to all kinds of bad behaviour. The calling code can end up completing and disposing the DbContext (what you are seeing) or if it calls another operation against the DbContext you could end up with an exception complaining about access across multiple threads since a DbContext detects that and does not allow access across threads.
You can observe the threading behaviour by inspecting Thread.CurrentThread.ManagedThreadId before the async method call, inside the async method, then after the async method call.
Without the await you would see Thread #X before the method call, then Thread #Y inside the async method, then back to Thread #X after. While debugging it would most likely appear to work since the worker thread would likely finish by the time you were done with the breakpoints, but at runtime that worker thread (Y) would have been started, but the code after the call on thread X would continue running, ultimately disposing of your DbContext likely before Thread Y was finished executing. With the await call, you would likely see Thread #X before the method call, Thread #Y inside, then Thread #Z after the call. The breakpoint after the async call would only be triggered after the async method completes. Thread #X would be released while waiting for the async method, but the DbContext etc. wouldn't be disposed until the resumption point created by awaiting the async method had run. It is possible that the code can resume on the original thread (X) if that thread is available in the pool.
This is a very good reason to follow the general naming convention for asynchronous methods to use the "Async" suffix for the method name. If your Mediator.Send method is async, consider naming it "SendAsync" to make missing await statements a lot more obvious. Also check those compiler warnings! If your project has a lot of warnings that are being ignored, this is a good reason to go through and clean them up so new warnings like this can be spotted quickly and fixed. This is one of the first things I do when starting with a new client is look at how many warnings the team has been ignoring and pointing out some of the nasty ones they weren't aware were lurking in the code base hidden by the noise.

How to resolve "Microsoft.AspNetCore.Server.Kestrel.Core.BadHttpRequestException: Reading the request body timed out due to data arriving too slowly"?

While doing API Performance Testing [e.g. No of Threads/Users : 50 with Loop Count : 10], 5 to 8 % samples with POST methods are failing due to below exception :
**Microsoft.AspNetCore.Server.Kestrel.Core.BadHttpRequestException: Reading the request body timed out due to data arriving too slowly. See MinRequestBodyDataRate.**
at Microsoft.AspNetCore.Server.Kestrel.Core.BadHttpRequestException.Throw(RequestRejectionReason reason)
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.Http1MessageBody.PumpAsync()
at System.IO.Pipelines.PipeCompletion.ThrowLatchedException()
at System.IO.Pipelines.Pipe.GetReadResult(ReadResult& result)
at System.IO.Pipelines.Pipe.GetReadAsyncResult()
at System.IO.Pipelines.Pipe.DefaultPipeReader.GetResult(Int16 token)
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.MessageBody.ReadAsync(Memory`1 buffer, CancellationToken cancellationToken)
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpRequestStream.ReadAsyncInternal(Memory`1 buffer, CancellationToken cancellationToken)
at Microsoft.AspNetCore.WebUtilities.FileBufferingReadStream.ReadAsync(Byte[] buffer, Int32 offset, Int32 count, CancellationToken cancellationToken)
at Microsoft.AspNetCore.WebUtilities.StreamHelperExtensions.DrainAsync(Stream stream, ArrayPool`1 bytePool, Nullable`1 limit, CancellationToken cancellationToken)
at Microsoft.AspNetCore.Mvc.Formatters.JsonInputFormatter.ReadRequestBodyAsync(InputFormatterContext context, Encoding encoding)
at Microsoft.AspNetCore.Mvc.ModelBinding.Binders.BodyModelBinder.BindModelAsync(ModelBindingContext bindingContext)
at Microsoft.AspNetCore.Mvc.ModelBinding.ParameterBinder.BindModelAsync(ActionContext actionContext, IModelBinder modelBinder, IValueProvider valueProvider, ParameterDescriptor parameter, ModelMetadata metadata, Object value)
Could you please suggest how i can fix this ?
NOTE :I correctly implemented async/await in POST method.This is
happening to POST method only and out of 500 samples only 20 to
40 samples are failing per POST method. Providing same inputs while
testing.
You could setKestrelServerLimits.MinRequestBodyDataRate property to null in your Program.cs as below:
.UseKestrel(opt =>
{
opt.Limits.MinRequestBodyDataRate = null;
});
Reference:
KestrelServerLimits.MinRequestBodyDataRate Property
Requests should be resolved fast, and this is a clear warning, that they are not. Exception is, that we are debugging/single-stepping the service, in which case I would prefer more tolerant request limits. In live environments, DDoS and even heavy load can render a system or service unstable very quickly, if limits are too high, because a growing request stack can overwhelm the system.
I made a cache service built on Kestrel and I found two scenarios which could cause this error:
The service received a massive amounts of items for a write operation on a quite busy DB table. This happened due to my own poor design. To resolve: either to optimize the update flow or create a per-task based update handler.
Service requests have stalled due to an instability, suspension or shut-down. (f.ex. TCP connections may not always shut down immediately using a static HttpClient, it sometimes fails to exit gracefully on shut-down)
Code below is from a NET 7.0 based kestrel service
// Limits can also be applied within appsettings.json,
appBuilder.WebHost.ConfigureKestrel(serverOptions =>
{
serverOptions.Limits.MaxConcurrentConnections = 100;
serverOptions.Limits.MaxConcurrentUpgradedConnections = 100;
#if !DEBUG
// Live: Demand higher request data-rate limits and low grace period, services can be unreliable.
// May cause requests to stall resulting in BadHttpRequestException.
// Catch them in your error handler
serverOptions.Limits.MinRequestBodyDataRate = new MinDataRate(
bytesPerSecond: 100,
gracePeriod: TimeSpan.FromSeconds(5)
);
#endif
});
Add error handler to app
// global error handler
app.UseMiddleware<ErrorHandlerMiddleware>();
Error handler:
public class ErrorHandlerMiddleware
{
private readonly RequestDelegate _next;
public ErrorHandlerMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext context)
{
try
{
await _next(context);
}
#if !DEBUG
catch (Microsoft.AspNetCore.Connections.ConnectionResetException)
{
// Not of interest - client has ended the connection prematurely. Happens all the time.
return;
}
#endif
catch (BadHttpRequestException bad) when (bad.Message.Contains("data arriving too slowly"))
{
#if DEBUG
// The original exception lacks insight.
throw new BadHttpRequestException($"Request data arriving too slowly: {context.Request.Path}");
#endif
}
}
}

How to a create a child thread or worker thread in Microsoft bot application

[Microsoft bot builder sdk]
How to a create a child thread or worker thread in Microsoft bot application which can continuously listen to a third party Web APi and post to users on different connectors.
I created a bot and want that bot to listen to a third party Web API continuously. This work should be done in a separate thread. But if I create a thread in a Dialog class it does not post responses to user as dialog object gets suspended as it wait for user input.
[Serializable]
public class SampleDialog : IDialog<object>
{
protected int count = 1;
[NonSerialized]
Thread ChildTask;
public async Task StartAsync(IDialogContext context)
{
ChildTask = new Thread(new ParameterizedThreadStart(RunChildTask));
ChildTask.Start(context);
context.Wait(MessageReceivedAsync);
}
private void RunChildTask(object context)
{
IDialogContext contex = context as IDialogContext;
while (true)
{
this.count++;
contex.PostAsync($"{this.count++}: You said From child");
Thread.Sleep(1000);
}
}
public virtual async Task MessageReceivedAsync(IDialogContext context, IAwaitable<IMessageActivity> argument)
{
var message = await argument;
if (message.Text == "reset")
{
PromptDialog.Confirm(
context,
AfterResetAsync,
"Are you sure you want to reset the count?",
"Didn't get that!",
promptStyle: PromptStyle.None);
}
else
{
await context.PostAsync($"{this.count++}: You said {message.Text}");
context.Wait(MessageReceivedAsync);
}
}
RunChildTask method is called but does not post back to user.
The reason it isn't working is because of the nature of the technology that the chatbot is implemented in. Chatbots use ASP.NET Web API, which like all other Web applications (e.g. WebForms or MVC) is stateless. That means that a request arrives from the client (the Bot Connector in this case), the request is processed by your chatbot, the chatbot responds, and the class instance for the chatbot becomes eligible for garbage collection. When the next request comes in, it creates another Web API instance for your chatbot.
Since the class instance no longer has references, neither do the services. The thread you just started no longer has a valid reference to the services. Therefore, you have a race condition where the normal processing of the chatbot finishes before the thread you started can successfully execute. Because it's a race condition, there might be times when the thread gets lucky and posts a message, which would probably be confusing when it works only sometimes. For this type of task, you should consider a more durable implementation like a cloud service or windows service on a VM.

MassTransit saga states receiving unexpected events

I am using MassTransit.Automatonymous (version 3.3.5 ) to manage a saga and I seem to be receiving unexpected events after a state has transitioned.
Here is my state set up:
Initially(
When(Requested)
.ThenAsync(InitialiseSaga)
.TransitionTo(Initialising)
);
During(Initialising,
When(InitialisationCompleted)
.ThenAsync(FetchTraceSetMetaData)
.TransitionTo(FetchingTraceSetMetaData)
);
During(FetchingTraceSetMetaData,
When(TraceSetMetaDataRetrieved)
.ThenAsync(ExtractTiffFiles)
.TransitionTo(ExtractingTiffFiles)
);
During(ExtractingTiffFiles,
When(TiffFilesExtracted)
.ThenAsync(DispatchTiffParseMessages)
.TransitionTo(DispatchingTiffParseMessages)
);
The error I sometimes receive is:
The TraceSetMetaDataRetrieved event is not handled during the
ExtractingTiffFiles state for the ImportTraceSetDataStateMachine state
machine
My understanding of how this should work at the point of the error is as follows:
During the FetchingTraceSetMetaData state, at some point I'll receive a TraceSetMetaDataRetrieved event. When this occurs, run the ExtractTiffFiles method, and transition to the ExtractingTiffFiles state.
Once in the ExtractingTiffFiles state, I wouldn't expect the TraceSetMetaDataRetrieved event since it's what got us into the ExtractingTiffFiles state.
The two FetchTraceSetMetaData and ExtractTiffFiles methods are as follows (truncated for brevity):
public async Task FetchTraceSetMetaData(BehaviorContext<ImportTraceSetDataSagaState, InitialisationCompleteEvent> context)
{
var traceSetId = context.Instance.TraceSetId;
_log.Information($"Getting pixel indicies for trace set with id {traceSetId}");
// Snip...!
await context.Publish(new TraceSetMetaDataRetrievedEvent { CorrelationId = context.Data.CorrelationId });
}
public async Task ExtractTiffFiles(BehaviorContext<ImportTraceSetDataSagaState, TraceSetMetaDataRetrievedEvent> context)
{
_log.Information($"Extracting tiffs for {context.Instance.TiffZipFileKey} and trace set with id {context.Instance.TraceSetId}");
// Snip...!
// Dispatch an event to put the saga in the next state where we dispatch the parse messages
await context.Publish(new TiffFilesExtractedEvent { CorrelationId = context.Data.CorrelationId });
}
Post posting pondering
It's just occurred to me that perhaps I should have my TransitionTo statements before my ThenAsync statements. e.g.
During(FetchingTraceSetMetaData,
When(TraceSetMetaDataRetrieved)
.TransitionTo(ExtractingTiffFiles)
.ThenAsync(ExtractTiffFiles)
);
Is that what I'm doing wrong?

What is the purpose of passing CancellationToken to Task.Factory.StartNew()

In the following code a CancellationToken is passed to the .StartNew(,) method as the 2nd parameter, but is only usable by the Action via the closure in the lambda. So, what is the purpose of passing the token through the .StartNew(,) method's 2nd parameter?
var cts = new CancellationTokenSource();
var token = cts.Token;
Task.Factory.StartNew(() =>
{
while (true)
{
// simulate doing something useful
Thread.Sleep(100);
}
}, token);
StartNew method schedules a task in the tread pool, but not necessary start it right at the moment, because threads may be unavailable. During waiting for the start the cancellation request may occur, after which the thread pool wouldn't start a task at all. After task was started it's your job to handle cancellation of the task.
Actually, the purpose of the CancellationToken passed to Task.Run and Taskfactory.StartNew is to allow the task to differentiate being cancelled by an exception thrown from CancellationToken.ThrowIfCancellationRequested and failing because of any other exception.
That is, if the CancellationToken passed at start throws, the task's state is Cancelled while any other exception (even from another CancellationToken) will set it to Faulted.
Also, if the CancellationToken is cancelled before the task actually starts, it won't be started at all.

Resources