0

Running a .NET Core 3.1 API with an async Controller Method that runs multiple DatabaseRepository async methods. I'm calling DatabaseRepository Tasks like this:

public async Task<ActionResult<MyResponse>> GetResultAsync([FromQuery] MyRequest request)
{
    var rootData = await _databaseRepository.GetRootDataAsync(request);
    //manipulate rootData
    var subscriptionsTask = _databaseRepository.GetSubscriptionsAsync(input1);
    var productsTask = _databaseRepository.GetProductsAsync(input1, input2);
    var assetsTask = _databaseRepository.GetAssetsAsync(input1, input3);
    //some other code
    var subscriptions = await subscriptionsTask;
    //some other code
    var products = await productsTask;
    var resources = await _databaseRepository.GetResourcesAsync(input2, input4);
    var assets = await assetsTask;
    //some other code
    return Ok(result);
}

DatabaseRepository method all look like this:

public async Task<IEnumerable<MyModel>> GetMyModelAsync(IEnumerable<string> input)
{
    //await Task.Delay(1);
    return await Elastic.Apm.Agent.Tracer
       .CurrentTransaction.CaptureSpan("MyQuery", "DB", async (span) =>
       {
           span.Labels["query"] = MyQuery;
           using var _dbConnection = new OracleConnection(connString);
           var myModels = await _dbConnection.QueryAsync<MyModel>(MyQuery,
               new
               {
                   input
               });
           return myModels;
       }, "my db query");
}

What I expected is that most of these async methods will run concurrently, but that doesn't happen. How do I know? With Elastic APM.

This is what I see:

https://cdn.discordapp.com/attachments/195830344715337728/757569429683830795/unknown.png

What do I do wrong?

wast
  • 878
  • 9
  • 27
  • 2
    Oracle driver actually is [not async](https://stackoverflow.com/a/29034291/2501279). – Guru Stron Sep 21 '20 at 11:54
  • @GuruStron that has nothing to do with the OP's question. – jeroenh Sep 21 '20 at 11:56
  • @jeroenh yes it is unless `Elastic.Apm.Agent.Tracer...` does not start new thread. See that `subscriptionsTask`, `productsTask` and `assetsTask` are all stared before being awaited. – Guru Stron Sep 21 '20 at 11:58
  • yes, you're right - I'm sorry! – jeroenh Sep 21 '20 at 11:59
  • @jeroenh I don't think that the [duplicate](https://stackoverflow.com/questions/7663101/c-sharp-5-async-await-is-it-concurrent) has anything to do with this question. The OP's expectation that the queries should run concurrently is correct. – Theodor Zoulias Sep 21 '20 at 12:01
  • Thanks Guru, didn't know that. However, If I add await Task.Delay(1); to one of the repository methods then concurrency happens. I can edit the question and add the screenshot from Elastic APM. – wast Sep 21 '20 at 12:03
  • @wast that because your code becomes actually async with continuations and stuff. You can achieve similar effect with calling oracle in explicit `Task.Run` – Guru Stron Sep 21 '20 at 12:05
  • The `await Task.Delay(1)` trick is an inefficient and unreliable alternative of `await Task.Yield()`. – Theodor Zoulias Sep 21 '20 at 12:08
  • @TheodorZoulias `await Task.Yield()` works but performance is worse than just running methods synchronously. – wast Sep 21 '20 at 12:20
  • IMHO comparing [sync vs async](https://stackoverflow.com/questions/36683468/can-using-async-await-give-you-any-performance-benefits) performance is unrelated to this question. – Theodor Zoulias Sep 21 '20 at 12:34
  • I agree. I tested a little and Yield is better than Delay(1). – wast Sep 21 '20 at 12:46
  • If you run your code in a project type that installs a `SynchronizationContext`, for example WinForms/WPF, none of the Delay/Yield tricks will have the desirable effect, because the continuation after the `await` will run on the captured context (the UI thread). For this reason it is probably preferable to offload explicitly the work to the `ThreadPool` by enclosing the problematic async methods in `Task.Run`s. Example: `var assetsTask = Task.Run(() => _databaseRepository.GetAssetsAsync(input1, input3));` – Theodor Zoulias Sep 21 '20 at 13:12
  • @TheodorZoulias you're right, I removed that reference. – jeroenh Sep 21 '20 at 17:41

0 Answers0