Race condition between commitTransaction returning and values being available on UI thread
See original GitHub issueGoal
There seems to be a race condition when propagating data between realms on different threads. Specifically, after calling commitTransaction on a background thread, the written data is not immediately available on the UI thread. See the code sample for more details.
Expected Results
I would expect that after commitTransaction returns, the data is written to the current Realm and reflected to other Realms. That does not appear to be the case.
Actual Results
When querying on the UI thread after posting back to a Handler, the data does not appear to be present.
Code Sample
Unfortunately, it’s slightly hard to create a simple code sample which reliably produces the problem. But I’ve attempted to describe it as concisely as possible here.
// On UiThread:
service.doSomeWork(new Callback() {
void findObject() {
try (Realm realm = Realm.getDefaultInstance()) {
MyObject obj = realm.where(MyObject.class).equals("id", mObjId).findFirst();
// Assert obj is not null.
}
}
}
// On background thread:
try (Realm realm = Realm.getDefaultInstance()) {
// Call server, receive MyObject obj in json.
realm.beginTransaction();
realm.createOrUpdateObjectFromJson(MyObject.class, json);
realm.commitTransaction();
}
mHandler.post(new Runnable() {
public void run() {
callback.findObject();
}
}
This works most of the time - but occasionally, will crash with a null object in findObject. If I replace the call to “mHandler.post” with “mHandler.postDelayed” with a tiny delay (2ms seems to work), everything works as expected reliably. As best I can tell here, there appears to be a slight delay between when commitTransaction returns and when the message synchronizing the UI thread’s Realm instance is actually sent/enqueued with the main thread handler.
In this case, it’s slightly awkward to solve the problem by using a RealmListener, because the object we’re waiting for doesn’t exist in Realm yet. We could use a query with expected size 1 instead of returning the object directly, but that’s not as clean to work with.
Version of Realm and tooling
Realm version(s): 3.3.1
Realm sync feature enabled: no
Android Studio version: 2.3.3
Which Android version and device: Any tested (seen on multiple devices/API levels)
Issue Analytics
- State:
- Created 6 years ago
- Comments:11 (7 by maintainers)
That’s because UI thread queries are evaluated asynchronously by default (especially if you use
async
query api), and there is no guarantee that they are immediately available after exactly one event loop later.But you can define a
RealmResults<MyObject> results = realm.where(MyObject.class).equalTo("id", id).findAllAsync();
and if you add a RealmChangeListener to it, it’ll be called when the item is inserted into Realm(also see https://github.com/realm/realm-java/issues/3427 and https://github.com/realm/realm-java/issues/4106 and https://github.com/realm/realm-java/pull/4515 )
Done - thanks, @Zhuinden !