realm / realm-java

Realm is a mobile database: a replacement for SQLite & ORMs
http://realm.io
Apache License 2.0
11.46k stars 1.75k forks source link

BadVersionException when adding changeListener #5656

Closed avsector closed 4 years ago

avsector commented 6 years ago

Goal

Registering a changeListener on RealmResults

Expected Results

No crash.

Actual Results

A BadVersionException happens. Here is the log:

Unable to start activity ComponentInfo{com.example.app/com.example.app.activity.ExampleActivity}: io.realm.internal.async.BadVersionException: std::exception in /home/cc/repo/realm/release/realm/realm-library/src/main/cpp/io_realm_internal_OsResults.cpp line 274

Steps & Code to Reproduce

Unfortunately I obtained this log from Crashlytics. I don't have the required steps to reproduce this crash.

Version of Realm and tooling

Realm version(s): 4.2.0

Realm sync feature enabled: No

Android Studio version: 3.0.0

Which Android version and device: This issue appears to be happening on BlackBerry devices (100% on Crashlytics console by the time I post this issue). Here's a list of devices: RIM Passport, Z10, Z30, and RIM Leap Android versions: 4.2.2 and 4.3

More info:

This crash started to happen when I updated Realm from 3.1.4 to 4.2.0. There was no such error before the update. Here is my realm configuration:

RealmConfiguration.Builder()
                .schemaVersion(4)
                .migration(new RealmMigrate())
                .build();

Let me know if you require any more data.

avsector commented 6 years ago

There is also another crash which happens on exactly the same devices:

Fatal Exception: io.realm.exceptions.RealmError: Unrecoverable error. Bad transaction log in /home/cc/repo/realm/release/realm/realm-library/src/main/cpp/io_realm_internal_OsSharedRealm.cpp line 126
       at io.realm.internal.OsSharedRealm.nativeBeginTransaction(OsSharedRealm.java)
       at io.realm.internal.OsSharedRealm.beginTransaction(OsSharedRealm.java:247)
       at io.realm.BaseRealm.beginTransaction(BaseRealm.java:389)
       at io.realm.Realm.beginTransaction(Realm.java:135)
       at io.realm.Realm.executeTransaction(Realm.java:1392)

This happens when I try to execute a synchronous transaction on a background thread.

nhachicha commented 6 years ago

Hi @mblcdr could you share the related Realm code (query etc) are you using async queries? do you perform the query in a background thread, IntentService etc?

The Bad transaction could be related since it's thrown when parsing the transaction log as part of an advance_read or promote_write

avsector commented 6 years ago

Here's the related query:

RealmResults<Message> results = realm.where(Message.class).equalTo("userId", userId)
                .findAllSortedAsync("timestamp");

As you can see we're using async queries.

We also use background threads to update related data. Here's an example:

realm.executeTransaction(new Realm.Transaction() {
            @Override
            public void execute(@NonNull Realm realm) {
                //Duplicate Check
                Message m = realm.where(Message.class).equalTo("id", id).findFirst();
                if (m != null)
                    return;

                m = realm.createObject(Message.class, id);
                //Updates some of the fields of m object
            }
});

The above code runs in a background thread. Also, I empathize on the matter that these crashes started to happen when we updated the Realm from version 3.1.4 to 4.2.0.

Thank you @nhachicha for taking the time to review our issue. Let me know if you need any other info, or I can help in any other way.

nhachicha commented 6 years ago

How are you managing the lifecycle of the thread doing the executeTransaction? are you closing the background Realm after the executeTransaction?

as you keep committing transactions, multiple versions of Realm are maintained, these snapshots will be reclaimed when no other thread is using the specific version, if one tries to read a reclaimed version then a bad version is thrown.

It's difficult to investigate without a reproducible use case (let alone it's specific to Blackberry)

my advice is to switch to executeTransactionAsync that uses the internal thread pool from Realm, this should simplify your logic as well, as you don't need to reason about your background thread.

Zhuinden commented 6 years ago

That thread pool is also just a regular thread pool though, so I seriously doubt that'll fix anything. 😕

nhachicha commented 6 years ago

Yes, except it manages internally the logic about the background Realm + uses the OsSharedRealm.VersionID from the same looper thread as the one doing the findAllSortedAsync. The idea is to try to narrow down the possibilities, by using only async queries/transaction from the same thread.

avsector commented 6 years ago

@nhachicha We use helper methods that open and close realm instances in try and finally blocks. As for thread lifecycle, we use a fixed thread pool for our write operations. Unfortunately, our logic is not flexible to be used with executeTransactionAsync.

We don't have BlackBerry devices to test. It would be best if we could test a sample app if someone is willing to test on mentioned devices.

clementetb commented 4 years ago

Closing due to missing information. Please comment in case you get more information and the issue still persists