-
-
Notifications
You must be signed in to change notification settings - Fork 8
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
[CRITICAL BUG] onSharedPreferenceChanged() does not fire reliably when multiple processes are writing at the same time #62
Comments
I'm curious if this also happens in the Android
If it's listened on the same process, it'll fire once it's committed to memory. If it's on a different process, the preference change will trigger once it's committed to disk.
That is totally a typo. I can make a new class with the correct name, and deprecate this class in the next version. Great catch! |
Hard to say whether this happens with the Android SharedPreferences since that doesn't work correctly with multiple processes, which why we're here in the first place. I'm not sure what thread deadlocking is, but if it's similar to a thread sleeping or busy doing work, shouldn't the listener still fire after the commit is done? It never ends up firing. Can this be fixed? Is it safe to use apply() instead or could this issue manifest with apply() as well if the timing is just right? |
Unfortunately after some more testing it looks like using apply() does not fix the issue. Steps to reproduce:
Expected output: Actual output: |
Found a cleaner way to demonstrate the bug that also fails more often. Have 2 processes write at the same time and listen for changes from both. Here are the steps to reproduce. 1 ) In the first process' onCreate (my case a service) run a loop that will write
Expected result:
Actual result: In my activity process I usually see both, but occasionally only the self change: This is a blocking bug for me! I can't use the library like this since I can't guarantee different processes are not writing at the same time. I can try to help look at the code for Harmony but I'm afraid I can't read Kotlin very well. |
I think I found the bug, and it has to do with the timing of the changes. Essentially, when you call the following on the first process:
You create two transactions in Harmony, each with their own timestamp. This gets stored, which notifies the other processes. So then what you have is this:
What happens in your scenario is that you create a new transaction by committing the event of testKey3 on the alternate process. This means that when testKey2 transaction comes in, it's considered "old" (since testKey3 is now the "last" transaction), and it's basically ignored for the change listener (but not for storage). To illustrate this situation:
I do want to make clear your data is stored. It's just that some logic meant to prevent re-emitting changed keys also was preventing the new keys from emitting in this particular circumstance. |
Good work! Whether or not it's saved, I still need the notifications to work reliably, or else one process will continue with old data from local memory and end up updating the shared prefs later without the change from the other process included. i.e. I don't check sharedPrefs every time I need a value. I have a local variable that is updated from shared prefs when there's a change notification. Plus the change may need to trigger certain code when it does change. Do my second and third ways to reproduce above also cover the same cause you found? Are you able to fix it relatively easily? |
I was seeing some occasional weirdness in my app after migrating over to Harmony. It looks like onSharedPreferenceChanged() does not fire in another process if that process happens to be committing at the same time.
Steps to reproduce:
The expected output is:
The actual output is USUALLY:
onSharedPreferenceChanged() does not fire for testKey2 changing at all!
I have also rarely seen:
... which would be fine, but this is not usually the case.
This does NOT happen to a listener in the same activity making the testKey1 and testKey2 commits.
This does NOT happen if you commit both keys at the same time:
This does NOT happen if using sharedPrefsEditor.apply() in the activity instead of commit(), but I have a feeling that's just because the write in the service is being called after the commit is already persisted to disk, so it's not really happening at the same time in this test.
P.S. apply() seems to be preferred since it is asynchronous and leaves the writing to the background. Does onSharedPreferenceChanged() only fire once it's persisted to disk? In that case, when is using commit() necessary or preferred over apply()?
P.S. Super minor but the regular SharedPreferences listener is named
OnSharedPreferenceChangeListener
. The Harmony version is namedOnHarmonySharedPreferenceChange**d**Listener
. Is this on purpose?The text was updated successfully, but these errors were encountered: