Cleanup Network.js code. Fix persisted request handling.#8306
Cleanup Network.js code. Fix persisted request handling.#8306
Conversation
Fix tests. Move enhanceParameters to its own method add docs fix bad comment clean up some logic that does not quite make sense Add more logging Only treat "Failed to fetch" errors as retryable for now. Curious to see what other kinds of errors we get. Throw any fetch errors when the response is not ok Make tests pass. Stop retrying non 200 jsonCode. fix up tests Add a few more tests Fix tests
|
cc @roryabraham too if you can give this a look |
| getLogger().info('Persisted optimistic request returned a valid jsonCode. Not retrying.'); | ||
| } | ||
|
|
||
| onResponse(request, response); |
There was a problem hiding this comment.
NAB: wouldn't be better to move onResponse to processRequest function?
There was a problem hiding this comment.
I'm gonna look into this in my next clean up PR. Basically there are two different processRequest happening here. One for regular requests run in the "main" queue and another for the persisted requests. Yes, I think there's probably a way to have just one handler for both. But I don't want to touch that yet.
| // If we are catching a known network error like "Failed to fetch" allow this request to be retried if we have retries left | ||
| if (error.message === CONST.ERROR.FAILED_TO_FETCH) { | ||
| const retryCount = NetworkRequestQueue.incrementRetries(request); | ||
| getLogger().info('Persisted request failed', false, {retryCount, command: request.command, error: error.message}); | ||
| if (retryCount >= CONST.NETWORK.MAX_REQUEST_RETRIES) { | ||
| getLogger().info('Request failed too many times removing from storage', false, {retryCount, command: request.command, error: error.message}); | ||
| NetworkRequestQueue.removeRetryableRequest(request); | ||
| } | ||
| } else if (error.name === CONST.ERROR.REQUEST_CANCELLED) { | ||
| getLogger().info('Persisted request was cancelled. Not retrying.'); | ||
| onError(request); | ||
| NetworkRequestQueue.removeRetryableRequest(request); | ||
| } else { | ||
| getLogger().alert(`${CONST.ERROR.ENSURE_BUGBOT} unknown error while retrying persisted request. Not retrying.`, { | ||
| command: request.command, | ||
| error: error.message, | ||
| }); |
There was a problem hiding this comment.
NAB: I think we can also move this logic to processRequest, no?
There was a problem hiding this comment.
Same answer as before.
| if (error.name === CONST.ERROR.REQUEST_CANCELLED) { | ||
| onError(queuedRequest, error); | ||
| return; | ||
| } |
There was a problem hiding this comment.
NAB: Also I wonder if we can / should handle this condition in processRequest 🤔
There was a problem hiding this comment.
Yes, but next PR please. And you can give me some ideas on how to do it if you have them 😄
marcochavezf
left a comment
There was a problem hiding this comment.
LGTM I just left a few comments that I think can be useful for the next refactoring PRs
| /** | ||
| * @param {String} newAuthToken | ||
| */ | ||
| function setAuthToken(newAuthToken) { |
There was a problem hiding this comment.
Why expose this function to set the authToken directly, instead updating SESSION.authToken in Onyx and relying on the Onyx.connect callback to update the local authToken?
There was a problem hiding this comment.
Good eye. It's not quite a hack, but close. Setting values in Onyx always takes at least one tick of the event loop and updates subscribers late. If you look at where this is used it might make more sense.
Lines 236 to 237 in 9c6c2a9
Inside reauthenticate() we are setting the "local" authToken since anything called after reauthenticate() will need immediate access to the authToken - otherwise it will use the old one again and get a 407 again.
I'll leave a comment here. Basically, Onyx being Onyx though.
There was a problem hiding this comment.
Thanks, makes sense.
Setting values in Onyx always takes at least one tick of the event loop and updates subscribers late ... Basically, Onyx being Onyx though
Is there a way we could fix this? Maybe we could create an issue in the Onyx repo?
There was a problem hiding this comment.
Yeah, I think we even tried once, but it broke things in unexpected ways and there is a high likelihood that it may break stuff again. Anyone using Onyx.merge() is expecting to have those values available on the next tick (or later). Our current tests are pretty dependent on that "feature" as well so it's gonna be interesting if we ever try and do anything about it.
There was a problem hiding this comment.
Hmmm seems to me like things might be a bit easier to reason about if one could safely assume than any value written in Onyx will be updated immediately, but I'll take your word for it.
There was a problem hiding this comment.
They would 1000% be easier. And we could still do it. But whoever does it will need to check everything carefully and probably remove a lot of waitForPromisesToResolve() calls from our tests 😂
There was a problem hiding this comment.
Created an issue: Expensify/react-native-onyx#123
It's not very detail-rich, but will hopefully explore more when I have some spare cycles.
| */ | ||
| function isAuthTokenRequired(command) { | ||
| return !_.contains([ | ||
| 'Log', |
There was a problem hiding this comment.
NAB alphabetize this list
src/libs/Network/index.js
Outdated
| const retryCount = NetworkRequestQueue.incrementRetries(queuedRequest); | ||
| getLogger().info('A retrieable request failed', false, { | ||
| retryCount, | ||
| // Retry and request that returns a "Failed to fetch" error. Very common if a user is offline or experiencing an unlikely scenario. |
There was a problem hiding this comment.
| // Retry and request that returns a "Failed to fetch" error. Very common if a user is offline or experiencing an unlikely scenario. | |
| // Retry any request that returns a "Failed to fetch" error. Very common if a user is offline. |
There was a problem hiding this comment.
Yes to the typo. Gonna leave the last part. There are other scenarios and they are unlikely.
There was a problem hiding this comment.
Added some of those scenarios.
| const originalXHR = HttpUtils.xhr; | ||
|
|
||
| beforeEach(() => { | ||
| HttpUtils.xhr = originalXHR; |
There was a problem hiding this comment.
NAB/nothing new, but isn't it weird that our HttpUtils reference xhr when really we use fetch?
There was a problem hiding this comment.
Seems like HttpUtils.request might be a more fitting name 🤷
|
No blockers for me, just had some questions / minor suggestions. All of this is good cleanup, thanks for helping to tame this difficult code @marcaaron 🙇 |
|
Thanks for the reviews ! Updated and tried to add some better comments about the confusing things. |
|
✋ This PR was not deployed to staging yet because QA is ongoing. It will be automatically deployed to staging after the next production release. |
|
🚀 Deployed to staging by @marcaaron in version: 1.1.47-0 🚀
|
|
🚀 Deployed to production by @roryabraham in version: 1.1.49-1 🚀
|
cc @marcochavezf
Details
This PR does some cleanup of the Network code to help get things ready for Sync Write Queue changes that we will be adding soon. I will be following up with more PRs to modularize and organize the code, but for this PR the changes are:
HTTP_STATUS_CODEconst withJSON_CODE(since astatuscode and ajsonCodeare NOT the same and this could easily confuse people)jsonCodecan just have the response handled byonResponse.catch(). I checked server logs and we have mostly thrownFailed to fetchand ajsonCode 407. I updated this logic so that only "Failed to fetch" errors will get "retried"ENSURE_BUGBOTlog line so we can see what these errors actually are and then decide how to handle them.fetch()is not tracking bad responses or doing anything about them. I added a throw toHttpUtils.xhr()(which will trigger theENSURE_BUGBOTso we can see what these things are and handle them).enhanceParametersand moved it to a separate file instead of using the "callback" trick.Onyx.connect()into a "store" file so that we can check for the "network ready" and update the localauthTokenfromAPI.js.Fixed Issues
No issue, but related to the Offline First doc preparation
Tests
Automated tests were added
Do QA steps
Verify that no errors appear in the JS console
QA Steps
Test behavior of offline queue