Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Request().WithShouldRetry is not called for failed requests. #190

Closed
spmanjunath opened this issue Dec 22, 2020 · 7 comments
Closed

Request().WithShouldRetry is not called for failed requests. #190

spmanjunath opened this issue Dec 22, 2020 · 7 comments
Assignees
Labels
Bug Something isn't working
Milestone

Comments

@spmanjunath
Copy link

spmanjunath commented Dec 22, 2020

Expected behavior

i am trying to overcome intermittent 409 error that occurs while uploading/updating metadata of a file in SharePoint's Document library using Microsoft Graph SDK. To retry failed calls SDK provides WithMaxRetry() and WithShouldRetry() options. The MaxRetry works for error codes 429, and I am assuming that ShouldRetry delegate offers us an option to implement our own evaluation logic for the retries. Based on this assumption, I have the below code:

_graphServiceClientFactory.GetClient().Drives[driveId].Root.ItemWithPath(path).ListItem.Fields.Request()
                       .WithShouldRetry((delay, attempt, httpResponse) =>
                        (attempt <= 5 &&
                        (httpResponse.StatusCode == HttpStatusCode.Conflict)))
                       .UpdateAsync(new FieldValueSet { AdditionalData = dataDictionary });

Actual behavior

In my test the ShouldRetry delegate is never evaluated on failed requests. I am not sure if the above usage is correct (i could not find any reference of its usage), please suggest otherwise.

Thanks.
AB#7188

@ghost ghost added the Needs: Triage label Dec 22, 2020
@spmanjunath
Copy link
Author

spmanjunath commented Jan 4, 2021

From reading RetryHandler.cs file there appears to be inconsistency in the way the conditional operator is used w.r.t. Retry in SendAsync() and SendRetryAsync() method. In the below SendAsync() method the ShouldRetry() and RetryOption.ShouldRetry() is subjected to 'and' conditional operator, whereas in the SendRetryAsync() method the ShouldRetry() and RetryOption.ShouldRetry() is evaluated with 'or' conditional operator.

SendAsync() method:

if (ShouldRetry(response) && httpRequest.IsBuffered() && RetryOption.MaxRetry > 0 && RetryOption.ShouldRetry(RetryOption.Delay, 0, response))
{
    response = await SendRetryAsync(response, cancellationToken);
}

SendRetryAsync() method:

 if (!ShouldRetry(response) || !request.IsBuffered() || !RetryOption.ShouldRetry(RetryOption.Delay, retryCount, response))
{
    return response;
}

I am not sure if the above difference is intentional, if the objective of WithShouldRetry() is to provide independent custom evaluation then ShouldRetry() and RetryOption.ShouldRetry() should be using 'or' operator. The SendRetry() method should therefore be as below,

protected async override Task<HttpResponseMessage> SendAsync(HttpRequestMessage httpRequest, CancellationToken cancellationToken)
{
    RetryOption = httpRequest.GetMiddlewareOption<RetryHandlerOption>() ?? RetryOption;

    var response = await base.SendAsync(httpRequest, cancellationToken);

    // Check whether retries are permitted and that the MaxRetry value is a non - negative, non - zero value
    if ((ShouldRetry(response)  || RetryOption.ShouldRetry(RetryOption.Delay, 0, response)) && httpRequest.IsBuffered() && RetryOption.MaxRetry > 0)
    {
        response = await SendRetryAsync(response, cancellationToken);
    }

    return response;
}              

@ddyett ddyett added the promote label Jan 11, 2021
@andrueastman
Copy link
Member

Hey, @spmanjunath!
Thanks for raising this issue.
It does indeed seem that if any custom codes not listed here are passed into ShouldRetry should retry delegate will not be evaluated due to the constraints placed at the moment as you have explained.
I will mark this as a bug and so that we can work towards having this resolved.

@andrueastman andrueastman added Bug Something isn't working and removed Needs: Triage labels Jan 13, 2021
@andrueastman andrueastman self-assigned this Jan 13, 2021
@andrueastman
Copy link
Member

Closed via #242

@andrueastman andrueastman added this to the 2.0.0 milestone Apr 22, 2021
@brcaswell
Copy link
Contributor

@andrueastman I think there is a misunderstanding here. This issue raised 2 observations:

However,

if (!ShouldRetry(response) || !request.IsBuffered() || !RetryOption.ShouldRetry(RetryOption.Delay, retryCount, response))
in SendRetryAsync should also be handled (in feature 2.0) and that handling should be consistent with SendAsync conditional check changes, right?

@brcaswell
Copy link
Contributor

brcaswell commented Jun 18, 2021

@andrueastman I pulled the latest head on feature/2.0 branch, updated RetryHandlerTest to perform a test that attempts more then 2 requests (1 request and the additional retries). that MockHttpMessageHandler type that is used in RetryHandlerTest needs to be deprecated. it only ever allowed for a 2 request scenario and it's unnecessary...

The updated test demonstrated SendRetryAsync returning after 1 Retry (i.e. failing to adher to MaxRetry in RetryHandlerOptions) - in respect and due to this logic check above.

see brcaswell#1 Pull Request that has these changes in it (in respect to branch off of feature 2.0 in forked repo)

branch and repo link brcaswell/msgraph-sdk-dotnet-core:update-retryhandler-test

@andrueastman
Copy link
Member

@brcaswell Thanks for explaining this so well with the sample PR.
Are you able to change this to target the feature/2.0 branch of this repo so that we may review and merge in the change?

@brcaswell
Copy link
Contributor

brcaswell commented Jun 19, 2021

@andrueastman yea, I can do that by creating a new PR.. it seems there is no supported way to retarget the base on that existing PR to target upstream repo branch. probably going to see duplicate mentions in this issue and referenced PR

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug Something isn't working
Projects
None yet
Development

No branches or pull requests

4 participants