-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Description
Hi,
I'm using Polly framework (namely Circuit Breaker and Retry policies) in an async context. I'm trying to integrate the Timeout
policy with it, but it isn't triggered.
I build a little POC, where we can see an example of the policy within a repository, being called by the WebApi.
The policy looks like:
public class ResiliencePolicy
{
public ResiliencePolicy()
{
Policy = Init();
}
public Policy Policy { get; }
protected virtual Policy Init()
{
var timeoutPolicy = Policy.TimeoutAsync(
TimeSpan.FromMilliseconds(100),
TimeoutStrategy.Pessimistic,
OnTimeoutAsync);
return timeoutPolicy;
}
protected virtual Task OnTimeoutAsync(Context context, TimeSpan timeSpan, Task task)
{
return Task.FromResult(0);
}
}
and the policy is used within the repository:
public class DocumentRepository : IDocumentRepository
{
private readonly ResiliencePolicy _resiliencePolicy;
private readonly IAmazonS3 _s3Client;
public DocumentRepository(IAmazonS3 s3Client, ResiliencePolicy resiliencePolicy)
{
_s3Client = s3Client;
_resiliencePolicy = resiliencePolicy;
}
public async Task<Document> GetDocumentAsync(string bucket, string objectName)
{
Document document = null;
var response =
await _resiliencePolicy.Policy.ExecuteAsync(async () =>
await _s3Client.GetObjectAsync(bucket, objectName));
if (response.HttpStatusCode == HttpStatusCode.OK)
document = new Document
{
Name = objectName,
Version = response.VersionId
};
return document;
}
}
With unit tests we can try to prove the timeout is triggered:
public class DocumentRepositoryTests
{
public DocumentRepositoryTests()
{
_clientMock = new Mock<IAmazonS3>();
_policy = new ResiliencePolicyStub();
_sut = new DocumentRepository(_clientMock.Object, _policy);
}
private readonly Mock<IAmazonS3> _clientMock;
private readonly ResiliencePolicyStub _policy;
private readonly DocumentRepository _sut;
[Fact]
public async Task GetDocumentAsync_WhenCallAboveThreshold_TriggersTimeout()
{
_clientMock.Setup(x =>
x.GetObjectAsync(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<CancellationToken>()))
.Callback((string b, string o, CancellationToken c) => { Thread.Sleep(2000); })
.ReturnsAsync(new GetObjectResponse());
await Record.ExceptionAsync(async () => await _sut.GetDocumentAsync("dummy", "document.json"));
_policy.TimeoutTriggered.Should().BeTrue();
}
}
with the ResiliencePolicyStub
as:
public class ResiliencePolicyStub : ResiliencePolicy
{
public bool TimeoutTriggered { get; private set; }
protected override async Task OnTimeoutAsync(Context context, TimeSpan timeSpan, Task task)
{
TimeoutTriggered = true;
await base.OnTimeoutAsync(context, timeSpan, task);
}
}
The IAmazonS3
mock has a callback to enforce the thread sleep, with a value above the timeout threshold. The delegate function is not triggered (the TimeoutTriggered
property doesn't change) neither it receives an exception (TimeoutException
).
From the samples there is another mechanism in place, however, from an API perspective the calls are async
by "default".
What is missing with the timeout policy configuration?
Note: the example was built with dotnet core 2.0.