The docs say that Execute is synchronous but it is awaitable asks for a Task as a parameter.
Sorry if this sounds dumb due to my C# async experience but I am a bit confused. For example: does this make sense ?
policy.Execute(() => Task.Run(() => MyTask()));
Hi @cemremengu .
Polly's ExecuteAsync() supports asynchronous executions: more information in the main Polly readme here. Depending on what you are aiming at, you might want something like:
await policy.ExecuteAsync(() => Task.Run(() => MyTask()));
If you can convert MyTask() so that it's a genuinely async method async Task MyTaskAsync() or similar, then you might want something like:
await policy.ExecuteAsync(() => MyTaskAsync());
To comment on: policy.Execute(() => Task.Run(() => MyTask())); : it doesn't make sense.
Task.Run(() => MyTask() on its own (disregarding Polly for a moment) creates and _immediately returns_ a Task representing MyTask() running.
The example doesn't capture that Task into any variable to observe later, nor synchronously wait on its outcome (eg .Wait(), .Result), nor asynchronously await it. It thus creates an orphaned task which risks an UnobservedTaskException if MyTask() throws.
policy.Execute(...) is synchronous, but it only governs Task.Run(...) near-immediately returning the Task representing MyTask() running. It won't capture any exceptions thrown by MyTask(), nor (if that was desired) wait synchronously or asynchronously for MyTask() to complete.
Hope this helps!
Thanks for this fantastic answer very informative! I was using await policy.ExecuteAsync(() => MyTaskAsync()); at first (which I will try again now) but I started to get a lot of connection timeouts from my Oracle db and using Execute(() => Task.Run() => ...) instead worked for some reason. I know this is probably due to a problem of the database but hopefully it will work this time with the changes you suggested.
EDIT: Nevermind :)
By the way, the syntax await policy.ExecuteAsync(() => MyTaskAsync()); does not compile for me. It complains that lambda function must have async modifier
No problem @cemremengu , glad it helped. Re your observation:
using
await policy.ExecuteAsync(() => MyTaskAsync());[...failed...] [...]
and usingExecute(() => Task.Run() => ...)instead worked for some reason.
If nothing else in the codebase or environment had changed, worth bearing in mind the possibility that Execute(() => Task.Run() => ...) in fact _didn't_ work. That is, it's possible the same exception was being thrown in the background by MyTask(), but you just didn't see it because the Execute(() => Task.Run() => ...) created an _unobserved task_ (see earlier comment).
Whether/when an UnobservedTaskException manifests (whether/when you see it) depends on a number of factors such as whether you have (or the environment you're running in has; eg if a unit test harness) anything hooked up to the UnobservedTaskException event; which version of .NET you're running (.NET40 and .NET45+ behave differently); whether the Task has been garbage-collected yet, etc.
(Obv you can tell more precisely from your environment whether it actually worked eg at the db level - I can't tell from here - it's just worth bearing in mind that possibility of unobserved failure, if the evidence for it appearing to work was (for example) only that no exception was thrown by the initial call)
That makes sense given that my service restarts occasionally without any error and I was wondering why 馃槂 I shouldnt have trusted my eyes. Thanks again, this information is gold.
I see that you attached a tag to the conversation. If you want I can add this to docs or readme as a pitfalls section ?
Glad @cemremengu if it's useful, and thanks for the doco idea! Since the async gotchas we've discussed are async general rather than specific to Polly, and already decently covered in the blogosphere/Stack Overflow, think I'll keep the Polly doco purely Polly-focused. _But_: thanks for the idea!
If you're looking for some good references on Task and async/await, some great starting points are:
Cheers
Most helpful comment
To comment on:
policy.Execute(() => Task.Run(() => MyTask()));: it doesn't make sense.Task.Run(() => MyTask()on its own (disregarding Polly for a moment) creates and _immediately returns_ aTaskrepresentingMyTask()running.The example doesn't capture that
Taskinto any variable to observe later, nor synchronously wait on its outcome (eg.Wait(),.Result), nor asynchronouslyawaitit. It thus creates an orphaned task which risks an UnobservedTaskException ifMyTask()throws.policy.Execute(...)is synchronous, but it only governsTask.Run(...)near-immediately returning theTaskrepresentingMyTask()running. It won't capture any exceptions thrown byMyTask(), nor (if that was desired) wait synchronously or asynchronously forMyTask()to complete.Hope this helps!