urql-graphql / urql

The highly customizable and versatile GraphQL client with which you add on features like normalized caching as you grow.
https://urql.dev/goto/docs
MIT License
8.61k stars 449 forks source link

RFC: Retry Exchange - Reset Delay #3220

Closed DoisKoh closed 1 year ago

DoisKoh commented 1 year ago

Summary

I believe it is not currently possible to reset the retry delay. For queries and mutations this makes sense, but when I have a subscription that I want to always be available, I would like to always retry it (retryIf => true for my retry exchange).

When there are network errors or for some reason my subscription breaks, I use a map exchange to check if its due to my JWT expiring (I'm using Hasura and it forcefully closes the websocket connection when the access token expires) - if it is, I trigger a refresh to get a new access token, and proceed to let the retry exchange attempt to retry the subscription. This works fine, however, after a few token expiries, I always end up hitting the maxDelayMs, which I'd like to be able to reset under certain conditions (for example, on a token refresh), otherwise I will always end up with a large gap in time where I'm not receiving my precious subscriptions.

Proposed Solution

If it is already possible, it would be nice if someone explained to me how to achieve this =)

Right now, it seems the retries simply accumulate the delayAmount * a random backoff factor until it reaches the maxDelayMs. If this delayAmount could be accessed from the operation context or something, I could modify it in other exchanges to reset it to the initial value.

Edit: I literally just noticed context.retryDelay in the source code. Sorry, I'm going to try messing with that value now, but anyway, this might be something useful to add into the docs for people with the same problem as me (which I believe should be common for subscriptions).

Edit Edit: Changing operation.context.retryDelay works but doing so asynchronously might cause some issues (it seems retryExchange makes a new operation and copies over the context values via spread operator, so the reference to it might get lost in some cases).

kitten commented 1 year ago

I think the general gist of this issue is rather: If a result is delivered for a given operation, the retry exchange should always reset and consider the operation as it was when it first came in.

In other words, when any operation has delivered any result, unless it's a mutation, the next set of retries should consider it a new operation and restart at the initial delay.

Does that sound about right? 🤔 I believe this should cover your case and should cover live and deferred queries

DoisKoh commented 1 year ago

Yea, sounds right to me.