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

Network Cleanup: Isolate "persisted " queue from "main" queue #8312

Merged
merged 12 commits into from
Mar 28, 2022

Conversation

marcaaron
Copy link
Contributor

@marcaaron marcaaron commented Mar 25, 2022

Details

Another round of Network refactoring. For this one, I focused on making the "persisted requests queue" more distinct from the "main request queue" with renames and making files more modular.

  • Renamed ONYXKEYS.NETWORK_REQUEST_QUEUE to PERSISTED_REQUESTS because that's what we use it for and "network request queue" is ambiguous
  • Tried to clean up the callbacks and by moving them to a NetworkEvents file
  • Killed the concept of the queue being "paused" or "unpaused" and instead we use an "isAuthenticating" flag and refer to it the same everywhere. This always confused me because the queue is processed in a "loop" but we don't actually ever "pause" the loop we just return early when we processing while authenticating.
  • Make the "Network is ready" hack more obvious by changing the name to hasReadRequiredDataFromStorage()
  • Moved the methods related to persisted requests OUT of the Network.js file so it is more isolated and less likely to be confused as related to the main queue code (still want to improve this further)
  • Separate "request retry counting" logic into a RetryCounter class
  • Move the processRequest() method to it's own file
  • Added extra documentation to Network/index.js and extracted the logic that pulls requests out of the main queue and into the persist queue and the logic for retrying main queue requests when they fail
  • Renamed actions/NetworkRequestQueue to PersistedRequests and gave it a separate "retry counter" instance (h/t @roryabraham) as that got a little mixed up in this PR, but seems like a fine addition to have for now. Though I am a little unsure how this would look for users who get a fetch error while making a blocking request. Seems like by perpetually retrying it would take a long time for them to see something "stop loading".
  • Also updated tests

Fixed Issues

No issue but related to Offline First project

Tests

Automated tests should cover most of these changes as we are mainly refactoring

  1. Run the app make sure things are working
  • Verify that no errors appear in the JS console

QA Steps

No specific QA as no behavior has changed, but keep a lookout for anything like loading spinners, offline stuff that was working not working, etc.

  • Verify that no errors appear in the JS console

@marcaaron marcaaron self-assigned this Mar 25, 2022
@marcaaron marcaaron changed the title [WIP] Network Cleanup Part 2 Network Cleanup Part 2 Mar 25, 2022
@marcaaron marcaaron changed the title Network Cleanup Part 2 Network Cleanup: Isolate "persisted " queue from "main" queue Mar 25, 2022
@marcaaron marcaaron marked this pull request as ready for review March 25, 2022 01:32
@marcaaron marcaaron requested a review from a team as a code owner March 25, 2022 01:32
@melvin-bot melvin-bot bot requested review from stitesExpensify and removed request for a team March 25, 2022 01:32
@marcaaron marcaaron removed the request for review from stitesExpensify March 25, 2022 01:33
Copy link
Contributor

@roryabraham roryabraham left a comment

Choose a reason for hiding this comment

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

Overall super stoked about these changes and very impressed by how much you've been able to unpack our Network code to make it more approachable. Really, excellent work. Did have a few comments, questions, and suggestions.

src/libs/Network/NetworkEvents.js Outdated Show resolved Hide resolved
src/libs/Network/NetworkEvents.js Show resolved Hide resolved
src/libs/Network/NetworkStore.js Outdated Show resolved Hide resolved
let networkReady = false;
let hasReadRequiredData = false;
let authenticating = false;
let isOffline = false;
Copy link
Contributor

Choose a reason for hiding this comment

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

We can do all this later (as part of offline-first doc) if you prefer, but should we assume that we're offline unless we know otherwise?

Suggested change
let isOffline = false;
let isOffline = true;

Further, if we made that change, could we remove the self-proclaimed hack for checkRequiredData?

  1. We assume that we're offline until we know otherwise
  2. We won't know otherwise (and set isOffline = false unless Onyx has triggered the Onyx.connect callback for the NETWORK key
  3. So we don't have to check if Onyx has read the authToken and credentials from storage before clearing the network lib to make request

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good thoughts. I don't have a clear answer about which would be better from an Offline First Doc perspective and would have to weigh some pros/cons.

I don't think we can remove that hack if we do this anyway as there's no guarantee we won't set isOffline to true before the credentials and authToken are ready. It's maybe adding some delay, but not the same as the guarantee we have now.

src/libs/Network/NetworkStore.js Show resolved Hide resolved
*/
export default function processRequest(request) {
const finalParameters = enhanceParameters(request.command, request.data);
const cancelRequestTimeoutTimer = NetworkEvents.startRequestTimeoutTimer();
Copy link
Contributor

Choose a reason for hiding this comment

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

Sorry, what happens if the request times out?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We "trigger a connection recheck", but I'll leave a comment

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I kinda move this so we wouldn't have to explain what happens and just emit and event. But the event is hardcoded in :D

src/libs/Network/PersistedRequestsQueue.js Outdated Show resolved Hide resolved
src/libs/Network/PersistedRequestsQueue.js Outdated Show resolved Hide resolved
src/libs/Network/PersistedRequestsQueue.js Outdated Show resolved Hide resolved
persistedRequestsQueueRunning = true;

// Ensure persistedRequests are read from storage before proceeding with the queue
const connectionId = Onyx.connect({
Copy link
Contributor

Choose a reason for hiding this comment

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

Sorry, it's probably just a me problem, but I'm confused how this is working and have a few questions:

  1. Why connect and disconnect instead of just relying on Onyx.connect to keep the persisted requests up-to-date? This feels like a hack b/c we're simulating Onyx.get here, though I'm sure it's a necessary one for now.
  2. What triggers the PersistedRequestsQueue to flush? The only usage I see is here, and that seems like it would only run on app init?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

You're right. It's a hack that ensures we don't start processing the queue until the requests have been read from storage. This mostly only matters on app init so we could probably improve this further (as we are subscribing in PersistedRequests as well and could use that). We might be able to use the createOnReadyTask to workaround this for now (but I'm not gonna add that here).

What triggers the PersistedRequestsQueue to flush? The only usage I see is here, and that seems like it would only run on app init?

Main usage here and runs when we go from offline to online

// Flush the queue when the connection resumes
NetworkEvents.onConnectivityResumed(flush);

@marcaaron
Copy link
Contributor Author

Thanks for the review @roryabraham. Updated and hopefully answered all the questions you had!

@roryabraham
Copy link
Contributor

Sounds like there are a few more improvements we could make, but we can do that in a follow-up PR(s). Thanks again for cleaning this up.

return Network.post(originalCommand, originalParameters, originalType);
}

// Prevent any more requests from being processed while authentication happens
Network.pauseRequestQueue();
isAuthenticating = true;
NetworkStore.setIsAuthenticating(true);
Copy link
Contributor

Choose a reason for hiding this comment

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

NAB: I think we can use NetworkStore.isAuthenticating() and NetworkStore.setAuthenticating(value) for this boolean value, no? https://airbnb.io/javascript/#accessors--boolean-prefix

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Maybe - though unsure if we follow that guidance strictly. I had it like that but changed it as I couldn't think of a way to do that without changing the variable name here...

let isAuthenticating = false;

Copy link
Contributor

Choose a reason for hiding this comment

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

I wouldn't see a problem renaming it to authenticating since it's only used as a private variable and the NetworkStore is a small file, but that's just my opinion :)

@marcaaron marcaaron merged commit 52a7e05 into main Mar 28, 2022
@marcaaron marcaaron deleted the marcaaron-networkCleanup2 branch March 28, 2022 18:26
@OSBotify
Copy link
Contributor

✋ This PR was not deployed to staging yet because QA is ongoing. It will be automatically deployed to staging after the next production release.

@OSBotify
Copy link
Contributor

🚀 Deployed to staging by @marcaaron in version: 1.1.47-0 🚀

platform result
🤖 android 🤖 success ✅
🖥 desktop 🖥 success ✅
🍎 iOS 🍎 success ✅
🕸 web 🕸 success ✅

@OSBotify
Copy link
Contributor

OSBotify commented Apr 5, 2022

🚀 Deployed to production by @roryabraham in version: 1.1.49-1 🚀

platform result
🤖 android 🤖 success ✅
🖥 desktop 🖥 success ✅
🍎 iOS 🍎 success ✅
🕸 web 🕸 success ✅

@Julesssss
Copy link
Contributor

Julesssss commented Apr 12, 2022

Hey, I'm slightly concerned that this PR may have introduced this exception (or at least, made the error more visible). This is the only recent PR I can see which touches the network code in the last couple of releases.

Specifically, I believe we're seeing one of these functions being called before being defined. Does that seem at all possible to you?

@marcaaron
Copy link
Contributor Author

Specifically, I believe we're seeing one of these functions being called before being defined. Does that seem at all possible to you?

Yes, and it's probably this one ->

unsubscribeFromNetInfo();

Is it possible that this has existed before? I don't think we changed anything about when the recheck callback should run and this PR mostly was a re-organization of existing logic.

@Julesssss
Copy link
Contributor

I'm slightly hesitant to move forward without better evidence, but yeah that does seem to be the case.

With the occurrences spiking on a single day and no specific code changes which directly modified the problematic area I think it's okay to continue with the release and continue to monitor the crash.

@Julesssss
Copy link
Contributor

FYI I submitted a quick PR and I'm moving on with the release.

@marcaaron
Copy link
Contributor Author

Thanks @Julesssss !

@sobitneupane
Copy link
Contributor

This PR might have caused #14584 regression.

@marcaaron
Copy link
Contributor Author

Thanks @sobitneupane. To clarify, it might have caused it or did cause it? If so, how? Can we figure it out?

@sobitneupane
Copy link
Contributor

I thought introduction of following code in PersistedRequests.js might have caused the issue.

persistedRequests = lodashUnionWith(persistedRequests, requestsToPersist, _.isEqual);

But it was there even before and introduced by 5cd8b4a commit.

@marcaaron
Copy link
Contributor Author

Got it, thanks for looking into that and clarifying.

I think these changes are old enough that there's nothing actionable and I checked off the steps here.

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.

6 participants