So this is what I do:
Now this is where the problem happens:
I'm on Android, but it's also happening to colleagues on iOS.
Realm is a MVCC database. This means that each thread gets its "own" version of Realm. We automatically update threads, but this is done using the Looper, so there is a delay.
If you use RealmChangeListeners, you _will_ be notified when the data is available, but if you use your own mechanism like an eventbus, that event might arrive before Realm has updated that thread.
I would recommend reading: https://news.realm.io/news/threading-deep-dive/
With 3.2 we re-added Realm.refresh() that will force any thread to the newest version, but it will also force all async queries to be synchronous, which might not be what you want.
@cmelchior Ok but just to be clear, the Realm instance for the network thread is created for the first time after I call commit on the previous transaction (so it's not a previously existing instance that's supposed to be updated). Is that still a normal behavior?
In that case, yes, the data should be in the Realm.
A Realm should always be at the latest version when:
1) Opening the Realm for the first time
2) You call beginTransaction()
3) You call realm.refresh()
Could the network thread be using an old Realm version by accident? E.g. if you are not closing the Realm again
Project onSuccess(Response response) throws IOException, JSONException, ParseException {
JSONObject projectJson = new JSONObject(response.body().string());
Realm realm = Realm.getDefaultInstance();
realm.beginTransaction();
Project project = realm.where(Project.class).equalTo("mServerId", projectJson.getInt("id")).findFirst();
project.update(projectJson);
realm.commitTransaction();
realm.close();
return project;
}
This is the complete callback which is running on the network thread. Realm sometimes returns null for the Project (which was created and commited on the main thread before even starting a network request). So I call both getInstance and beginTransaction, and always close Realm afterwards.
NOTE: I'm using OkHttp, which uses a ThreadPoolExecutor, which reuses threads, so in theory it might be possible that I get called on the thread used for previous network call, and that Realm already has an instance for that thread. But I also always call close (and begin transaction).. So that shouldn't be happening..
Yes, queries inside transactions should always be up to date. Are you 100% the data wasn't perhaps deleted in the meantime?
@cmelchior My mistake, posted you the wrong code, in the request that was a problem I did a query before beginTransaction.
But still, the only case it should cause a problem is if OkHttp reused a thread, and Realm already had an instance for it.. But I always call close.. So still shouldn't be happening.
Anyways, I'll see if querying after beginTransaction fixes it.. But it's still weird.
Thanks for all the info for now.
If you onSuccess callback crashes, it might never close the instance. I would wrap it in a try-finally
@cmelchior Already did, in a super-class all my requests extend from 😄
Btw, your support around here is just amazing, congratulations and thank you! @cmelchior
On background non-looper threads, querying inside transaction guarantees that you always see the latest version of the data.
Realm 3.2.0 (re-)introduced realm.refresh() which allows a background non-looper thread to refresh itself to the latest data even if it was stuck on a previous version. Although with general use, that tends to happen if a local Realm instance is unclosed, so the global Realm instance stays open as well.
@gajicm93 Sounds like you have got answers so I am closing the issue. As always, don't hesitate to create a new issue.
:ok:
Most helpful comment
On background non-looper threads, querying inside transaction guarantees that you always see the latest version of the data.
Realm 3.2.0 (re-)introduced
realm.refresh()which allows a background non-looper thread to refresh itself to the latest data even if it was stuck on a previous version. Although with general use, that tends to happen if a local Realm instance is unclosed, so the global Realm instance stays open as well.