vpv
vpv

Reputation: 938

How can I await multiple tasks at the same time inside policy circuit breaker in c#?

I wasn't able to find an answer to this question. Please help.
Scenario
I can wait for multiple tasks at a time by using Task.WhenAll. But I want it inside a policy circuit breaker execution method. Below is the code snippet. I am calling an API 2 times and wanted to get the call results at a time. Without circuit breaker, it is working, and I can use the results later in my code. But I do not understand how I can call the API using Task.WhenAll inside circuit breaker.

var param1 = new Param
{
    Category = Category.Manager,
    FirstName = "John",
    Address = "Sydney",
};
var param2 = new Param
{
    Category = Category.Officer,
    FirstName = "Adam",
    Address = "Melbourne",
};

Working code:

var results1 = Task.WhenAll(_apiClient.GetAsync(param1), _apiClient.GetAsync(param2));
//At this point results1 is NOT null and contains 2 items in it

Problem:

var results2 = _policyCircuitBreaker.Execute(()=> Task.WhenAll(_apiClient.GetAsync(param1), _apiClient.GetAsync(param2)));
//at this point results2 is of type Task, how can I get the results2 similar to results1

Upvotes: 1

Views: 389

Answers (2)

mountain traveller
mountain traveller

Reputation: 8156

In general, as Johnathon Sullinger says, if you have a Task<TResult> and want to obtain the result of that Task in a non-blocking way, await-ing that Task<TResult> (or using a continuation) is the way to go.

Assuming however your circuit-breaker is from Polly, the formulation from the original question (repeated below) will not give the async circuit-breaker behaviour you likely want:

var results = _policyCircuitBreaker.Execute(() => Task.WhenAll(param1Task, param2Task));

That formulation uses the synchronous .Execute(...): the .Execute(...) call through _policyCircuitBreaker will thus only govern Task.WhenAll(...) synchronously (near-immediately) returning the Task<[]> which represents waiting for the inner tasks to complete. The circuit-breaker will not (in this formulation) govern your calls to _apiClient.GetAsync(...).

Assuming your intention is that the circuit-breaker should govern both the calls _apiClient.GetAsync(param1) and _apiClient.GetAsync(param2) (ie respond to faults those calls may raise), you need to use Polly's async-native policies and .ExecuteAsync() overloads:

var results3 = await _policyCircuitBreaker.ExecuteAsync(() => Task.WhenAll(_apiClient.GetAsync(param1), _apiClient.GetAsync(param2))));

.ExecuteAsync(...) internally awaits the delegate you have supplied while applying the policy behaviour (in this case circuit-breaker behaviour) to it.

Upvotes: 2

Johnathon Sullinger
Johnathon Sullinger

Reputation: 7414

You will need to store a reference to the Tasks outside of your _policyCircuitBreaker call.

var param1Task = _apiClient.GetAsync(param1);
var param2Task = _apiClient.GetAsync(param2);

Task results = _policyCircuitBreaker.Execute(() => Task.WhenAll(param1Task, param2Task));

Either await the circut breaker call, or use a continuation to get the results when they are completed.

Await example:

await results;
var param1ApiResult = param1Task.Result;
var param2ApiResult = param2Task.Result;

Continuation example (for code paths that can't be awaited)

results.ContinueWith(finishedTask =>
{
    var param1ApiResult = param1Task.Result; 
    var param2ApiResult = param2Task.Result;
});

Both examples will give you the results - you just need to store a reference to each Task, before you execute them in the WhenAll call. That lets you get their results later once the Tasks have completed.

I'm making some assumptions because your post wasn't clear as to what API you are trying to use nor what the Execute methods API looks like.

Upvotes: 0

Related Questions