Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Exception Handling with akka.net PipeTo() on Task

Referring to Akka.Net documentation, using PipeTo() is preferred when dealing with asynchronous jobs.

When dealing with a function that returns Task<T>, I can handle the failure event, no problem.

The problem is, when dealing with a function that does not return any type, but only Task, one still calls the PipeTo function, but instead of having the overload which contains the failure handle, it now says the following: 'As this task has no result, only exceptions will be piped to the recipient.'.

Does this mean, if I have the following code:

public class RepositoryComponent : IRepositoryComponent
{
    private SqlSettings _sqlSettings;

    public RepositoryComponent(SqlSettings sqlSettings)
    {
        _sqlSettings = sqlSettings;
    }

    public async Task InsertJobAsync(RetryModel job)
    {
        try
        {
            await... //some logic
        }
        catch { throw; }
    }
}

My actor:

public class RepositoryActor : ActorBase
{
    private IRepositoryComponent _repoComponent;
    private ActorSelection _retryActor;

    public RepositoryActor(IRepositoryComponent repoComponent) : base()
    {
        _repoComponent = repoComponent;
    }

    public override void Listening()
    {
        Receive<RepositoryMessages.GenericRequestNoReturn>(x => InvokeRequest(x));
        Receive<RepositoryMessages.GenericRequestWithResponseType>(x => InvokeRequestAndSendResponse(x));
    }

    private void InvokeRequest(RepositoryMessages.GenericRequestNoReturn msg)
    {
        try
        {
            //some logic with msg
            _repoComponent.InsertJobAsync(new Core.Models.RetryModel()).PipeTo(Context.Self);
        }
        catch (Exception ex)
        {
            base.ExceptionHandling(ex);
        }
    }
}

In order to catch exceptions in the above actor, I need to add another Receive handler to cater for exceptions, as for example, below:

Receive<Exception>(x => SomeExceptionHandler(x));

Is this correct? If so, no need for the try {} catch {} around my code block?

like image 842
monstertjie_za Avatar asked Feb 15 '18 08:02

monstertjie_za


1 Answers

If you're calling your asynchronous operation with PipeTo, it won't block your current code execution path. This means that the try/catch statement will never be executed (as success or failure of the task representing asynchronous operation will be redirected to Self).

There are 2 ways to handle exceptions that may happen during async operation execution:

  1. Use ReceiveAsync<> instead of Receive<> in combination with async lambda - this will allow you to simply await for asynchronous operation to complete. You're free to use try/catch in this context, as it will work as expected. Keep in mind that this will keep your actor non-reentrant - it means, that the actor will not process any messages until current asynchronous operation completes.
  2. Keep PipeTo and add another Receive<> handler in your logic - in case when a task wrapping asynchronous operation will fail, the exception will be wrapped into a message and redirected back to PipeTo's target (Self in your case). By default exceptions produced this way are wrapped in Status.Failure message - in this case you'd need to add Receive<Status.Failure> handler in your actor. You can also specify a custom message constructor used to handle PipeTo failures i.e.: _repoComponent.InsertJobAsync(input).PipeTo(Context.Self, failure: exception => new MyErrorMessage(exception))
like image 189
Bartosz Sypytkowski Avatar answered Nov 15 '22 04:11

Bartosz Sypytkowski