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

feat: allow overriding retry validator at request level #876

Merged
merged 7 commits into from
Sep 23, 2022

Conversation

cemreyavuz
Copy link
Contributor

@cemreyavuz cemreyavuz commented Sep 14, 2022

DX-907

In brief, the motivation is allowing to override retry behavior at the request level. Slack thread can be checked for more background.

In the PR, I extended the http request options with a field called retryValidator. It can be either a function or a boolean with the value false. If it is false, we don't retry at all. If it is a function, we call it to decide whether retry or not. Else, we use the default retry validator.

There are a few ways we can go for the solution. I went with the generic solution to include extra options for requests inside the BasicHttpClient. However, I wouldn't say I'm too happy with it. We need to pass the extra props for retry validator basically everywhere.

Instead of extending BasicCogniteClient to add retry functionality, we can implement this inside that class with an option to disable globally (which is actually the idea with RetryableHttpClient in the end as well). This might be one alternative.

@cemreyavuz cemreyavuz changed the title chore: wip chore: allow overriding retry validator at request level Sep 14, 2022
@cemreyavuz cemreyavuz changed the title chore: allow overriding retry validator at request level feat: allow overriding retry validator at request level Sep 20, 2022
@cemreyavuz cemreyavuz marked this pull request as ready for review September 20, 2022 08:25
@cemreyavuz cemreyavuz requested a review from a team September 20, 2022 08:25
@cemreyavuz
Copy link
Contributor Author

Integration test probably fails because of the actual api

Comment on lines 31 to 87
const retryValidator =
typeof request.retryValidator === 'function'
? request.retryValidator
: this.retryValidator;

const shouldRetry =
request.retryValidator !== false &&
retryValidator(request, response, retryCount);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is basically the whole change, rest is just type updates

@polomani
Copy link
Collaborator

@cemreyavuz what's the use case? what do you actually want to achieve?

@cemreyavuz
Copy link
Contributor Author

cemreyavuz commented Sep 20, 2022

Forgot to add the slack thread link: https://cognitedata.slack.com/archives/CG10VQPFX/p1663074669728439. In the most basic sense, I want to override the retry behavior for some requests. I want to stop retrying for some, I want to change the retry behavior for some other (like, retry 3 times instead of 5, or change the back-off mechanism).

@BugGambit
Copy link
Contributor

What about adding an optional retryValidator?: RetryValidator field here: https://github.com/cognitedata/cognite-sdk-js/blob/master/packages/core/src/baseCogniteClient.ts#L30-L50
And modify the getRetryValidator method here to check if the user passed a custom retryValidator?
Wouldn't that solve the problem without propagating changes down to BasicHttpClient?

@cemreyavuz
Copy link
Contributor Author

cemreyavuz commented Sep 21, 2022

What about adding an optional retryValidator?: RetryValidator field here: https://github.com/cognitedata/cognite-sdk-js/blob/master/packages/core/src/baseCogniteClient.ts#L30-L50
And modify the getRetryValidator method here to check if the user passed a custom retryValidator?
Wouldn't that solve the problem without propagating changes down to BasicHttpClient?

I think we should also add that option "in addition" to this one - that was actually going to be my next PR.

I agree that it is a bit complex to propagate these changes down to BasicHttpClient - but I think we should still give the option to stop retrying per request. We don't have to override the function itself, so it can be a boolean. But even that wouldn't simplify this code necessarily.

You might ask why overriding the global retry validator is not enough. The fusion specific reason is that there are specific needs for each subapp for retries. The main motivation for doing this at request level is not retrying rather "unimportant" requests to unblock other "important" requests. One example from transformations sub app: we make tons of requests to raw api for fetching the tables in databases for tooltips on sidebar. When the number of requests gets bigger, we start getting 429s for raw api requests. However, we actually make some other requests to raw api in the same page that is more critical for UI to function. So when we handle these at request level, either we can stop retrying lower priority requests - or we can higher the back-off period to let higher priority requests go through first.

Also another problem with having one retry validator on global level is that we can only manage that in sdk-singleton. Subapps wouldn't be able to change the behavior at all. So, subapp developers would go into sdk-singleton and update the validator as it fits their use cases. And, there is still prioritization problem specific to each subapp. So, overall I still think we should have an option to either stop or override retrying at request level.

Copy link
Contributor

@BugGambit BugGambit left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I left a small comment.

I get the point that a global retry validator is not sufficient for every use case.
But I still believe we should not modify BasicHttpClient. That is just an abstraction for Axios/fetch etc., and shouldn't care about retries. If we do that, we mix the abstraction levels.
I suggest to modify RetryableHttpClient where we override the http-methods (get, post, put, delete, patch) from BasicHttpClient providing an additional option to have a custom retry validator.

What do you think?

packages/core/src/httpClient/basicHttpClient.ts Outdated Show resolved Hide resolved
@cemreyavuz
Copy link
Contributor Author

cemreyavuz commented Sep 22, 2022

I left a small comment.

I get the point that a global retry validator is not sufficient for every use case.
But I still believe we should not modify BasicHttpClient. That is just an abstraction for Axios/fetch etc., and shouldn't care about retries. If we do that, we mix the abstraction levels.
I suggest to modify RetryableHttpClient where we override the http-methods (get, post, put, delete, patch) from BasicHttpClient providing an additional option to have a custom retry validator.

What do you think?

I agree on not modifying BasicHttpClient, that's why I went the way of extending type with ExtraOptions for requests . But doing all of these changes inside RetryableHttpClient also works - actually it's even better imo. We have more control over it this way and it's less error prone for the future.

(I'll also add a few tests for this new functionality - but wanted to get the review for the actual code in the meanwhile)

@cemreyavuz
Copy link
Contributor Author

@BugGambit we can also add a test to check if default max retry attemps number is used - but the same problem applies here as well: #878 (comment). I'm open for suggestions if there is a way to make it work

Copy link
Contributor

@BugGambit BugGambit left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice. I liked this approach better. I left a few comments.

packages/core/src/httpClient/retryableHttpClient.ts Outdated Show resolved Hide resolved
HttpRequestRetryValidatorOptions;

type HttpRequestRetryValidatorOptions = {
retryValidator?: false | RetryValidator;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So the behaviour is as follows:

  • null/undefined -> default retrier
  • false -> no retries at all
  • function -> custom retrier
    ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, the motivation was flexibility - but of course that comes with a small cost on cognitive complexity.

We can even go one step further and add the option of numbers for retry counts in the future as well - sth like this: https://tanstack.com/query/v4/docs/guides/query-retries. I didn't want to have it as I was forcing a max retry count - but if we won't have it, we can have that option as well. In that case the behaviour would be:

  • undefined-> default retry validator
  • false -> no retry
  • function -> custom retry validator
  • number -> just check retry count and retry if it doesn't exceed the argument

@cemreyavuz
Copy link
Contributor Author

cemreyavuz commented Sep 23, 2022

@BugGambit should I also bump the version, or does ci/cd do it automatically? (i think it does, so I'm not changing)

@codecov
Copy link

codecov bot commented Sep 23, 2022

Codecov Report

Merging #876 (f520340) into master (de3557f) will decrease coverage by 4.54%.
The diff coverage is 69.24%.

@@            Coverage Diff             @@
##           master     #876      +/-   ##
==========================================
- Coverage   84.06%   79.52%   -4.55%     
==========================================
  Files          70      105      +35     
  Lines        1952     3389    +1437     
  Branches      249      510     +261     
==========================================
+ Hits         1641     2695    +1054     
- Misses        301      671     +370     
- Partials       10       23      +13     
Impacted Files Coverage Δ
packages/beta/src/cogniteClient.ts 100.00% <ø> (ø)
packages/core/src/httpClient/httpError.ts 90.00% <ø> (ø)
packages/core/src/testUtils.ts 35.59% <0.00%> (-19.58%) ⬇️
packages/stable/src/__tests__/testUtils.ts 96.42% <ø> (-3.58%) ⬇️
packages/stable/src/api/3d/assetMappings3DApi.ts 79.16% <ø> (-20.84%) ⬇️
packages/stable/src/api/3d/nodes3DApi.ts 81.81% <ø> (-18.19%) ⬇️
packages/stable/src/api/3d/revealNodes3DApi.ts 100.00% <ø> (ø)
packages/stable/src/api/3d/revealSectors3DApi.ts 100.00% <ø> (ø)
packages/stable/src/api/3d/revisions3DApi.ts 95.65% <ø> (-4.35%) ⬇️
...kages/stable/src/api/annotations/annotationsApi.ts 100.00% <ø> (ø)
... and 124 more

Copy link
Contributor

@BugGambit BugGambit left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM 🚀

@BugGambit
Copy link
Contributor

@BugGambit should I also bump the version, or does ci/cd do it automatically? (i think it does, so I'm not changing)

Correct. It will bump it automatically based on the commit messages.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants