redux-persist icon indicating copy to clipboard operation
redux-persist copied to clipboard

Data sporadically erased (React Native)

Open jln-dk opened this issue 6 years ago • 37 comments

Introduction

We are building a React Native app, and we are using redux-persist together with redux-persist-filesystem-storage to store data on the phone.

The issue

We are experiencing that sometimes after an app update (via TestFlight and Play Store) all the app data just is erased. The app is simply back to initial state. This can happen for both iOS and Android.

Steps to reproduce

Well, this is where it get's annoying.. We haven't yet been able to get the exact steps to reproduce the issue. It just happens sporadically. We have around 100 beta testers, and ~20 people have reported this issue. Both on iOS and Android, and there was no link between a specific app update version or anything.

Similar issues

We have of course looked around on GitHub, trying to find similar issues. At first we thought https://github.com/rt2zz/redux-persist/issues/199 was our issue, so that's why we switched to redux-persist-filesystem-storage from AsyncStorage. But that didn't help. And it looks like other people are still experiencing it as well: https://github.com/robwalkerco/redux-persist-filesystem-storage/issues/2

Additional information

Final note

Although this issue is somewhat vaguely described, I'm submitting it to raise awareness, because we think it's a rather important issue if it really IS an existing bug. Maybe someone out there is looking for help with the same issue - so here it is.

I have created the same issue on redux-persist-filesystem-storage: https://github.com/robwalkerco/redux-persist-filesystem-storage/issues/14

jln-dk avatar Apr 26 '18 04:04 jln-dk

+1 !

ghost avatar May 09 '18 09:05 ghost

We're actually having the exact same problem in my company. We are just using AsyncStorage though. @jesperlndk did you find a solution? Or a different way to store the data?

I've also noticed sometimes we get the following error:

{
  err: Error: redux-persist: persist timed out for persist key "myKey" at blob:http://localhost:8081/d0182199-b2a0-4806-ad16-47af460661d8:18512:45,
  key: "myKey"
  payload: undefined
  type: "persist/REHYDRATE"
}

alexmngn avatar Jun 18 '18 10:06 alexmngn

Same here. I started looking into it and I think what's happening is the getItem() Promise on app load rejects with an error. That error is passed to the REHYDRATE action. The action does not consider the error and just writes undefined to the store, causing the setItem middleware to run (because it detected a "change") and "erasing" the data.

I am currently looking for issues with a suggested fix or a PR. Might make my own PR but I'm not sure what correct behavior should be. Probably just to not write anything to local store if getItem failed. And I might need to add a new error handler to the config so consumer can handle when a rehydration error occurs.

jrmurad avatar Jun 21 '18 22:06 jrmurad

@jrmurad I moved from AsyncStorage to FSStorage (based on react-native-fs) and the issue seem to appear way less often, but it still occurs from time to time on some Android devices. An appropriate fix would be to have a way to detect and display they error and stop the rehydrate process.

alanlanglois avatar Jun 26 '18 22:06 alanlanglois

Same issue, any updates?

lilosir avatar Aug 21 '18 13:08 lilosir

I can confirm that this still happens when using FSStorage. It happens of both v5 & v4. Seems to have something to do with the autoRehydrate which is not getting logged and data not persisted in some cases.

lukebrandonfarrell avatar Sep 12 '18 08:09 lukebrandonfarrell

After more testing it seems to be working with redux-persist-filesystem-storage without erasing data randomly. It could be something to do with AsyncStorage. @lilosir

lukebrandonfarrell avatar Sep 12 '18 09:09 lukebrandonfarrell

Anyone found a solution @jrmurad @jesperlndk @alexmngn?

alanlanglois avatar Sep 14 '18 16:09 alanlanglois

@alanlanglois Using redux-persist-filesystem-storage worked for me. Using in production.

lukebrandonfarrell avatar Sep 15 '18 10:09 lukebrandonfarrell

@lukebrandonfarrell Few mounths ago I gave it a try, unfortunatly it wasn't working with an expo detached project. (rn-fetch-blob wasn't at least). I then changed a little the lib to use react-native-fs instead of rn-fetch-blob. It fixed the problem in most cases, but I still have the issue from time to time (way less often). Do you have a large audience are you sure it totally fixed your problem? (I got like 10K+ install on Android)

alanlanglois avatar Sep 17 '18 16:09 alanlanglois

I was able to reproduce the issue by setting timeout to something incredibly low like 10ms. The store was consistently overwritten by initialState. That's when I tested a few weeks ago, and I didn't write a test. I'd like to write a test to reproduce it, but I wanted to mention that in case anyone else is able to investigate further.

timeout is a key in the persist config, but it is not yet documented in the README.

ssorallen avatar Sep 17 '18 17:09 ssorallen

@ssorallen It seems to be talked here: https://github.com/rt2zz/redux-persist/issues/717

alanlanglois avatar Sep 18 '18 07:09 alanlanglois

redux-persist's timeout functionality definitely wipes out the store and rehydrates it with initial state. I don't know if that's the only issue here, but I have confirmed locally and in my own app that it does wipe out my store.

You can disable the timeout functionality entirely by passing timeout: 0 in your persistConfig. If your store takes a while to rehydrate then the PersistGate will continue to show the loading state. If your store never rehydrates then the PersistGate will never go away. However, it won't ever hit the timeout functionality and wipe your store. Your users can refresh the app or whatever it takes in case that slow rehydration was transient.

In your persist config, prevent the timeout setup (persistReducer.js#L88-99) (the default timeout value is 5000):

const persistConfig: {
   ...
   timeout: 0, // The code base checks for falsy, so 0 disables
};

A complete solution for redux-persist would be to add a timeout component to PersistGate that would render if your timeout is hit and stop the rehydration work. What it shouldn't do is then rehydrate your store with initialState, which is the current functionality.

ssorallen avatar Nov 10 '18 15:11 ssorallen

Happening on my app as well! Some of my users have reported to me that all their journal data disappeared. Have not been able to reproduce.

"react": "16.8.3",
"react-native": "0.59.8",
"react-redux": "^6.0.0", 
"redux": "^4.0.1",
"redux-persist": "^5.10.0",
"redux-thunk": "^2.3.0",

wmonecke avatar Jun 11 '19 12:06 wmonecke

I've been working on an Expo-based React Native app that encountered the same problem.

After trying the timeout fix mentioned above and finding that didn't work for us, we ended up using redux-persist-expo-fs-storage and haven't been experiencing the same problems since.

If you're using Expo, I'd recommend trying that out as your storage solution. Just for reference, here's some more package.json info:

    "react": "16.5.0",
    "react-native": "https://github.com/expo/react-native/archive/sdk-32.0.0.tar.gz",
    "react-redux": "^5.1.0",
    "redux": "^4.0.1",
    "redux-persist": "^5.10.0",
    "redux-persist-expo-fs-storage": "^1.2.2",
    "redux-thunk": "^2.3.0",

alxmrtnz avatar Jun 11 '19 13:06 alxmrtnz

This bug is also happening in our production app. We have a lot of users complaining because their data (stored using the library: redux-persist-fs-storage), is being sporadically erased... We tried adjusting the timeout config, but that didn't solve the problem. We are not using Expo. If anyone solved this issue, please help me with the solution. Many thanks!!!!

webytecno avatar Apr 24 '20 20:04 webytecno

We're experiencing the same issue as well now (for a couple of months). I can give this information (which is about the same as the OP)

  • It only happens on app update
  • From our support tickets, seems to primarily happen on Android (we're pretty sure it's android only - but not 100% sure)
  • We though we were hitting the timeout limit, which we disabled. We also added extra performance logging of the persistStore function when rehydrating. The longest execution time we had was 887ms (in production), so it's not hitting the timeout at all. Pretty sure that can be ruled out.
  • Also switched from AsyncStorage to redux-persist-filesystem-storage , doesn't seem to make a difference.

If there's any way we can help out (by providing logs, setups, etc) please contact me. It's becoming a big problem for us and our users.

svenlombaert avatar Jul 10 '20 13:07 svenlombaert

Is this repo maintained any more? These issues date back a full two years now, and the last commit to master was September 2019.

If this is affecting your production application and causing data loss, it might be time to find a replacement for redux-persist.

ssorallen avatar Jul 10 '20 15:07 ssorallen

Well, I think why people are still posting all the issues here is because when you're using redux, there doesn't seem to be a lot of alternative to redux-persist.

svenlombaert avatar Jul 10 '20 15:07 svenlombaert

In my case explicitly setting the whitelisted reducers, as well as timeout to 0 in redux persist config actually helped. No more store data loss on app restart after that.

Orange9000 avatar Jul 28 '20 14:07 Orange9000

I think by now, I've tried every possible combination of workarounds. They always get persisted and rehydrated except on app update. We implemented our own solution now to persist a specific reducer which contained data that never gets synced with the server.

svenlombaert avatar Jul 28 '20 14:07 svenlombaert

Btw this issue drove us to build our own persist library (a year ago): https://github.com/aspect-apps/redux-persist-machine

As we got frustrated with little issues like this, we wanted a much simpler implementation, with more customisation over the data we load, and how we load it.

lukebrandonfarrell avatar Jul 28 '20 16:07 lukebrandonfarrell

Does anyone have an update on this? This is actually making us lose customers.

wmonecke avatar Feb 02 '21 21:02 wmonecke

Any fix on this issue?

ArchanaNair avatar Feb 04 '21 14:02 ArchanaNair

@wmonecke @ArchanaNair If redux-persist is making you lose customers, I’d likely abandon this library. It has not been maintained in several years at this point. (see dates on issues and comments)

You might have more success with major version 5, but if these are decisions that impact your revenue then this library does not seem reliable enough.

ssorallen avatar Feb 04 '21 19:02 ssorallen

I managed to identify and solve the same issue on Android using redux persist with async storage. Here is what the problem was in my case: As @jrmurad mentioned the error manifests itself on getItem. To identify it I patched the persistReducer.js as follows and threw an error instead of rehydrating with undefined:

diff --git a/node_modules/redux-persist/lib/persistReducer.js b/node_modules/redux-persist/lib/persistReducer.js
index 1116881..6aed0e7 100644
--- a/node_modules/redux-persist/lib/persistReducer.js
+++ b/node_modules/redux-persist/lib/persistReducer.js
@@ -94,6 +94,7 @@ function persistReducer(config, baseReducer) {
       if (typeof action.rehydrate !== 'function' || typeof action.register !== 'function') throw new Error('redux-persist: either rehydrate or register is not a function on the PERSIST action. This can happen if the action is being replayed. This is an unexplored use case, please open an issue and we will figure out a resolution.');
       action.register(config.key);
       getStoredState(config).then(function (restoredState) {
+        console.log('redux persist reducer')
         var migrate = config.migrate || function (s, v) {
           return Promise.resolve(s);
         };
@@ -102,11 +103,14 @@ function persistReducer(config, baseReducer) {
           _rehydrate(migratedState);
         }, function (migrateErr) {
           if (process.env.NODE_ENV !== 'production' && migrateErr) console.error('redux-persist: migration error', migrateErr);
-
-          _rehydrate(undefined, migrateErr);
+          console.log('redux persist migration error', migrateErr)
+          throw migrateErr
+          // _rehydrate(undefined, migrateErr);
         });
       }, function (err) {
-        _rehydrate(undefined, err);
+        console.log('redux persist error', err)
+        throw err
+        // _rehydrate(undefined, err);
       });
       return _objectSpread({}, baseReducer(restState, action), {
         _persist: {

In my case this yielded an error sometime later (due to this error being not very repeatable) as follows:

redux persist error [Error: Row too big to fit into CursorWindow requiredPos=0, totalRows=1] This lead me to the local SQLite issue of trying to read big text entries for single rows exceeding the default of 4mb for a single row, and that indeed was the issue. A few thousand rows of text were being stored in the redux store under a single row.

Saving this as a JSON file and storing a reference to that file in redux solved the issue. You could expand that limit but I figured it's probably there for good reason. I hope this saves someone else.

Shared credit to @Federkun

StuartGough7 avatar May 26 '21 15:05 StuartGough7

@ssorallen I have been searching for a replacement with no success!

wmonecke avatar Jul 11 '21 17:07 wmonecke

I am getting this issue where i store the userType in the redux-persist but when ever the app is updated it loses all its data and the app becomes un useable the user have to log out in order to store the userType from the api i have been looking for hours but still was'nt able to find a solution

Shazil1 avatar Jul 16 '21 10:07 Shazil1

We are still facing this, seems like this repo is not actively maintained

r1jsheth avatar Aug 17 '21 12:08 r1jsheth

I think by now, I've tried every possible combination of workarounds. They always get persisted and rehydrated except on app update. We implemented our own solution now to persist a specific reducer which contained data that never gets synced with the server.

Would you mind sharing what did you do?

r1jsheth avatar Aug 17 '21 13:08 r1jsheth