You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[HOLD for payment 2024-11-20] Investigate infinite credentials Onyx loading state when migrating SignInPage to useOnyx #48725
When migrating SignInPage.tsx to useOnyx for testing purposes, @blazejkustra noticed that the screen becomes blank when logging out the user. Refreshing the page solves the problem.
It seems that the credentials data returned by useOnyx is always on loading state, so if we put the isLoadingOnyxValue condition to only render the page when all Onyx data is available the page will never render.
Thanks for this report @blazejkustra , I will look into it tomorrow because I'm not sure if my fix will cover this and maybe the problem is a broader one than expected.
After cracking my head over these issues for some time, here's my findings:
Case 1
Problem
Racing conditions between an Onyx.clear() execution and useOnyx subscription causes useOnyx to be always in loading state and never fully resolve its first connection.
This happens because we clear the cache during Onyx.clear() execution and if we are connecting to some key with useOnyx at the same time, there is no cache where the hook can look into (OnyxCache.hasCacheForKey(key) will be false) and it get stuck in loading state.
From what I've seen this problem doesn't happen for withOnyx basically because the racing condition doesn't occur (maybe because it's a component and it's slightly slower during initialisation?).
Solution
No definitive solutions yet. I first tried to use OnyxCache.captureTask() in Onyx.clear() to "register" this ongoing task and later I would make the subscription / useOnyx processes wait until clearing is done to proceed with the process, but I'm not sure if it's a good solution.
EDIT: I came with a similar but simpler solution in the Draft PR.
Case 2
Problem
The cache clearing performed by Onyx.clear() combined with the connection reusing logic we currently have causes an useOnyx connection to a key that was connected before to be always loading state and never fully resolve its first connection.
Specifically, if we are reusing the connection of a key that is still exists in the connection manager after an Onyx.clear() execution, the next useOnyx to connect to that same key gets stuck in the loading state as the cache doesn't really exist anymore, and we didn't populate it again because we are reusing the connection.
When logging out a lot of Onyx connections will be removed from the connection manager as expected, but some of them persists because they don't belong to the React's lifecycle e.g. any Onyx.connect in our lib files, helping create the issue described above.
This problem doesn't happen for withOnyx because the HOC doesn't reuse connections and create new ones upon every subscription, thus populating the cache again for that key and allowing the HOC to work as expected.
Solution
I first tried to "signal" the connection manager after Onyx.clear() that it should refresh its own connections (by temporally subscribing to the key and updating the cached value) when a subscriber was going to reuse them again, but it led to many bugs and overall it looked like a bad / unpredictable solution.
My second and current approach is to introduce the concept of "Session ID" to the connection manager. This Session ID will be used when generating the Connection IDs with generateConnectionID() and we would generate another Session ID after an Onyx.clear() call. In this way, if a subscriber tries to connect to a Onyx key that persisted after Onyx.clear() it would result in a separate connection instead of reusing the connection from the "previous session".
Not sure if it's the best approach but it solves this case for now. Draft PR of the solution: Expensify/react-native-onyx#588
I will continue exploring for other solutions and also explore if we can solve this at the cache's side. I will reach out Margelo's team eventually too as they have more context about the cache system and can suggest better solutions.
The solution for this issue has been 🚀 deployed to production 🚀 in version 9.0.60-3 and is now subject to a 7-day regression period 📆. Here is the list of pull requests that resolve this issue:
Skipping the payment summary for this issue since all the assignees are employees or vendors. If this is incorrect, please manually add the payment summary SO.
Activity
fabioh8010 commentedon Sep 11, 2024
Update: Still investigating it, it seems a problem around the cache / Onyx.clear()
fabioh8010 commentedon Sep 18, 2024
Update: I got back to this issue today and I was able to replicate through an new unit test, will investigate and work in the solution now.
fabioh8010 commentedon Sep 20, 2024
Update: Still looking for a solution to solve the racing condition around the connection / cache / Onyx.clear()
fabioh8010 commentedon Sep 26, 2024
Update: Working on and testing a possible solution I found today.
blazejkustra commentedon Oct 1, 2024
I found another example for this when working on #49103, here is a diff for you to test it:
The outcome is that there is an indefinite loading because one of the keys is in constant loading state:
Screen.Recording.2024-10-01.at.09.17.33.mov
fabioh8010 commentedon Oct 1, 2024
Thanks for this report @blazejkustra , I will look into it tomorrow because I'm not sure if my fix will cover this and maybe the problem is a broader one than expected.
blazejkustra commentedon Oct 3, 2024
@fabioh8010 Any updates?
fabioh8010 commentedon Oct 8, 2024
Hi @blazejkustra, sorry for the late here.
After cracking my head over these issues for some time, here's my findings:
Case 1
Problem
Racing conditions between an
Onyx.clear()execution anduseOnyxsubscription causesuseOnyxto be always inloadingstate and never fully resolve its first connection.This happens because we clear the cache during
Onyx.clear()execution and if we are connecting to some key withuseOnyxat the same time, there is no cache where the hook can look into (OnyxCache.hasCacheForKey(key)will befalse) and it get stuck inloadingstate.From what I've seen this problem doesn't happen for
withOnyxbasically because the racing condition doesn't occur (maybe because it's a component and it's slightly slower during initialisation?).Solution
No definitive solutions yet. I first tried to use
OnyxCache.captureTask()inOnyx.clear()to "register" this ongoing task and later I would make the subscription /useOnyxprocesses wait until clearing is done to proceed with the process, but I'm not sure if it's a good solution.EDIT: I came with a similar but simpler solution in the Draft PR.
Case 2
Problem
The cache clearing performed by
Onyx.clear()combined with the connection reusing logic we currently have causes anuseOnyxconnection to a key that was connected before to be alwaysloadingstate and never fully resolve its first connection.Specifically, if we are reusing the connection of a key that is still exists in the connection manager after an
Onyx.clear()execution, the nextuseOnyxto connect to that same key gets stuck in theloadingstate as the cache doesn't really exist anymore, and we didn't populate it again because we are reusing the connection.When logging out a lot of Onyx connections will be removed from the connection manager as expected, but some of them persists because they don't belong to the React's lifecycle e.g. any
Onyx.connectin our lib files, helping create the issue described above.This problem doesn't happen for
withOnyxbecause the HOC doesn't reuse connections and create new ones upon every subscription, thus populating the cache again for that key and allowing the HOC to work as expected.Solution
I first tried to "signal" the connection manager after
Onyx.clear()that it should refresh its own connections (by temporally subscribing to the key and updating the cached value) when a subscriber was going to reuse them again, but it led to many bugs and overall it looked like a bad / unpredictable solution.My second and current approach is to introduce the concept of "Session ID" to the connection manager. This Session ID will be used when generating the Connection IDs with
generateConnectionID()and we would generate another Session ID after anOnyx.clear()call. In this way, if a subscriber tries to connect to a Onyx key that persisted afterOnyx.clear()it would result in a separate connection instead of reusing the connection from the "previous session".Not sure if it's the best approach but it solves this case for now. Draft PR of the solution: Expensify/react-native-onyx#588
I will continue exploring for other solutions and also explore if we can solve this at the cache's side. I will reach out Margelo's team eventually too as they have more context about the cache system and can suggest better solutions.
fabioh8010 commentedon Oct 14, 2024
Update: I've designed a similar but simpler solution for Case 1, tomorrow will open the PR and ask for review
fabioh8010 commentedon Oct 18, 2024
Update: Asked for more eyes here. I will proceed into opening the PR to Exfy review next week and add screenshots / videos
fabioh8010 commentedon Oct 22, 2024
PR was opened to review
9 remaining items
melvin-bot commentedon Nov 13, 2024
Reviewinglabel has been removed, please complete the "BugZero Checklist".melvin-bot commentedon Nov 13, 2024
The solution for this issue has been 🚀 deployed to production 🚀 in version 9.0.60-3 and is now subject to a 7-day regression period 📆. Here is the list of pull requests that resolve this issue:
If no regressions arise, payment will be issued on 2024-11-20. 🎊
For reference, here are some details about the assignees on this issue:
melvin-bot commentedon Nov 20, 2024
Skipping the payment summary for this issue since all the assignees are employees or vendors. If this is incorrect, please manually add the payment summary SO.
marcaaron commentedon Nov 21, 2024
Seems maybe @c3024 requires payment here before we can close? I'm gonna add the Bug label here.
melvin-bot commentedon Nov 21, 2024
Triggered auto assignment to @bfitzexpensify (
Bug), see https://stackoverflow.com/c/expensify/questions/14418 for more details. Please add this bug to a GH project, as outlined in the SO.bfitzexpensify commentedon Nov 22, 2024
Offer sent @c3024
bfitzexpensify commentedon Nov 22, 2024
And now paid out - let's close this out.