EN VI

C# - Types of asynchronous background code execution in REST API endpoint?

2024-03-15 23:00:15
How to C# - Types of asynchronous background code execution in REST API endpoint

I am implementing a REST API endpoint in ASP.NET, that should do the following:

  1. Asynchronously start a long-running algorithm
  2. Return 202 Accepted immediately after starting the algorithm (no await)

This is what the endpoint looks like:

app.MapPut(
    "/api/instance/{id}",
    (int id, InstanceDto instanceDto, IInstanceRunningService service) =>
    {
        // Start the execution asynchronously and return ASAP
        service.RunInstance(instanceDto);
        return TypedResults.Accepted($"/api/instance/{id}");
    })

This is what the RunInstance method in the service looks like:

public async Task RunInstance(InstanceDto instanceDto)
{
    // Asynchronously call another API that we are starting the execution of the algorithm
    // We do not need to wait for the response, we just want to start the algorithm ASAP (again no await)
    _ = this.AnotherApiClient.PutAsync(instanceDto);

    // Compute the algorithm and await the result
    var result = await Algorithm.Run(instanceDto.Parameters);
    
    // Finally call the other API again to notify that the computation finished
    await this.AnotherApiClient.PutAsync(instanceDto);
}

The order of operations I want to achieve is:

  1. Return 202 Accepted
  2. Notify another API that computation is starting (we don't care how this API call ends, because we will be sending another one in step 4.)
  3. Compute the algorithm
  4. Notify another API that computation has finished

I can't figure out the best way to achieve this. There are so many options:

  • service.RunInstance(instanceDto);
  • service.RunInstance(instanceDto).Start();
  • Task.Run(() => service.RunInstance(instanceDto))
  • Make RunInstance an asynchronous delegate and do delegate.BeginInvoke()
  • Create a Thread or use ThreadPool
  • Maybe implement the service differently with more async methods

Solution:

I can't figure out the best way to achieve this. There are so many options:

I would argue that none of them is "the best". The common approach for such task is to use some kind of background processing. The "simple" one is to just use the build-in one with hosted services, especially check out the Queued background tasks option (you can instantiate several "processors" to handle the same queue - for example see this answer).

Also do not pass the resolved dependencies (i.e. IInstanceRunningService service) to you handler task/queue since their lifetime is controlled by the lifetime of the original request and disposable services will be disposed with it, create scope manually and resolve everything needed from it - see the Consuming a scoped service in a background task section of the docs for example.

Alternatively you can look into using more advanced schedulers like Hangfire or Quartz and/or leveraging asynchronous processing with queue/service bus.

Answer

Login


Forgot Your Password?

Create Account


Lost your password? Please enter your email address. You will receive a link to create a new password.

Reset Password

Back to login