question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Timeout not working as expected [EDIT: Async pessimistic timeout does not timeout synchronous delegates]

See original GitHub issue

Please clarify if I am misunderstanding how the Timeout Policy works. I would expect this to cause a TimeoutRejectedException but it does not. I have a Task.Delay(4000).Wait() defined in the func that Polly executes. I would think that anything in that Func that takes longer than the specified Timeout would throw TimeoutRejectedException but this is not the behavior I’m seeing.

Taken from Polly samples

            // Define our timeout policy: time out after 2 seconds.  We will use the pessimistic timeout strategy, which forces a timeout - even when the underlying delegate doesn't support it.
            var timeoutPolicy = Policy
                .TimeoutAsync(
                    TimeSpan.FromSeconds(2),
                    TimeoutStrategy.Pessimistic,
                    onTimeoutAsync: (callContext, span, task) =>
                    {
                        return Task.CompletedTask;
                    }
                );

            // Define our waitAndRetry policy: keep retrying with 4 second gaps.  This is (intentionally) too long: to demonstrate that the timeout policy will time out on this before waiting for the retry.
            var waitAndRetryPolicy = Policy
                .Handle<Exception>() // Exception filtering!  We don't retry if the inner circuit-breaker judges the underlying system is out of commission!
                .WaitAndRetryForeverAsync(
//                    attempt => TimeSpan.FromSeconds(4),
                      attempt => TimeSpan.FromMilliseconds(100),
                  (exception, calculatedWaitDuration) =>
                    {
                    });

            FallbackPolicy<String> fallbackForAnyException = Policy<String>
                .Handle<Exception>()
                .FallbackAsync(
                    fallbackAction: /* Demonstrates fallback action/func syntax */ async ct =>
                    {
                    },
                    onFallbackAsync: async e =>
                    {
                    }
                );

            PolicyWrap<String> policyWrap = fallbackForAnyException.WrapAsync(timeoutPolicy).WrapAsync(waitAndRetryPolicy);

            while (!Console.KeyAvailable && !cancellationToken.IsCancellationRequested)
            {
                try
                {
                    Func<CancellationToken, Task<string>> foo = (ct) =>
                    {
                        //shouldn't this cause a TimeoutRejectedException???
                        Task.Delay(4000).Wait();  
                        return Task.FromResult("hello");
                    };

                    string msg = await policyWrap.ExecuteAsync(foo, cancellationToken);
                }
                catch (Exception e)
                {
                }

                // Wait half second
//                await Task.Delay(TimeSpan.FromSeconds(0.5), cancellationToken);
            }

Issue Analytics

  • State:closed
  • Created 6 years ago
  • Comments:9 (6 by maintainers)

github_iconTop GitHub Comments

1reaction
reisenbergercommented, Nov 25, 2017

@udlose Let us know (after Thanksgiving!) if we can help any further on this. Re your question:

how does the calling code “catch” the timeout? [in TimeoutStrategy.Optimistic]

… there could be two senses.

(1) Per previous comment, a TimeoutRejectedException is thrown back onto the code placing the call through TimeoutPolicy. (2) If the question is what happened to the task being executed (which was timed out…) : in TimeoutStrategy.Optimistic, that task should honour the CancellationToken and thus cancel itself (it won’t continue running). If it wants to pass information back to the calling code about its state at the time of cancellation, that could be done by throwing a custom OperationCanceledException containing extra information. That exception is already passed back to calling code as the inner exception of the TimeoutRejectedException. If we extend Polly per #338, that exception would also be passed to onTimeout/Async, which might be a better place eg to capture info for logging.

0reactions
reisenbergercommented, Jun 25, 2019

Np. Re:

my example code is arguably what you would expect to work just from looking at it

Agreed. There was a trade-off at design time. TimeoutStrategy.Pessimistic works for your sample code, but it is more expensive in terms of resource consumption (especially for the sync case). We made TimeoutStrategy.Optimistic the default case, since it seemed good practice to encourage developers towards co-operative cancellation by CancellationToken (which consumes less resource), rather than making the sledge-hammer version (TimeoutStrategy.Pessimistic) the default - which also has (for users to grok) complications about how to catch errors from the walked-away-from Tasks. (Edit: in case this seems like magic information to anyone coming later to the thread, it is all covered in more detail in the doco.)

That’s just to explain the rationale of the decision (why your case doesn’t work as the default), but I agree: it makes for this speedbump on learning how to use the policy.

Read more comments on GitHub >

github_iconTop Results From Across the Web

c# - How to enable timeout policy with polly
Pessimistic mode with asynchronous policies is intentionally designed only to govern delegates which conform to the normal async pattern. ...
Read more >
Polly 7.2.4
Pessimistic timeout allows calling code to 'walk away' from waiting for an executed delegate to complete, even if it does not support cancellation....
Read more >
On awaiting a task with a timeout in C# - The Old New Thing
Say you have an awaitable object, and you want to await it, but with a timeout. How would you build that? What you...
Read more >
Running an async task with a timeout
I wrote a function async(timeoutAfter:work:). Its goal is to run an async task with a timeout. If the timeout expires and the work...
Read more >
Concise handling of async tasks with timeouts in c# - ...
The sync and async codes are handled differently so you have to be ... The Timeout policy can work in two modes: optimistic...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found