question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Write transactions increase Realm version number on each call

See original GitHub issue

Hi! This is more of a question of understanding how to keep realm versions low

I’m using the following code to write to the database:

override suspend fun save(e: Entry): Entry = Entry(db.write { upsert(e) })

// equal to, using inline functions:

suspend fun save(e: Entry): Entry = realm.write {
     val rid: RealmUUID = e.id
     
     val update: EntryEntity.() -> Unit = {
          property = e.property
     }
     val entity = query<EntryEntity>("id == $0", rid).first().find() ?: EntryEntity().apply { id = rid }
     copyToRealm(entity.apply(update), UpdatePolicy.ERROR)
}.mapToEntryDataClass() // maps the object to a domain model immediately outside of the db.write {} block

The save() function is invoked on a background thread (Dispatchers.IO or Dispatchers.Default)

I’m using a global singleton instance provided with Koin to my app

single(createdAtStart = true) { provideDatabase() } // <-- provideDatabase() creates and opens a Realm instance

And a few of the DAO objects, singletons too, accessed on random dispatchers.

But while queries run on Dispatchers.IO using this code:

internal suspend inline fun <T : BaseRealmObject> RealmQuery<T>.findSuspend(
    context: CoroutineContext = Dispatchers.IO
) = withContext(context) { find() }

// result is immediately mapped to domain object

all run fine, each write operation produces the following Logcat output:

     D  Updating Realm version: VersionId(version=50) -> VersionId(version=51)
     D  Closing unreferenced version: 49
     D  Updating Realm version: VersionId(version=51) -> VersionId(version=51)
     D  Updating Realm version: VersionId(version=51) -> VersionId(version=52)
     // each write produces 2 version increments
     D  Updating Realm version: VersionId(version=52) -> VersionId(version=52)
     D  Updating Realm version: VersionId(version=52) -> VersionId(version=53)
     
     D  Updating Realm version: VersionId(version=53) -> VersionId(version=53)
     D  Updating Realm version: VersionId(version=53) -> VersionId(version=54)
     
     D  Updating Realm version: VersionId(version=54) -> VersionId(version=54)
     D  Updating Realm version: VersionId(version=54) -> VersionId(version=55)
     
     D  Updating Realm version: VersionId(version=55) -> VersionId(version=55)
     D  Updating Realm version: VersionId(version=55) -> VersionId(version=56)
     
     D  Updating Realm version: VersionId(version=56) -> VersionId(version=56)
     
     // this is output immediately before crash
     D  Closing unreferenced version: 55
     D  Closing unreferenced version: 54
     D  Closing unreferenced version: 55
     D  Closing unreferenced version: 53
     D  Closing unreferenced version: 54
     D  Closing unreferenced version: 52
     D  Closing unreferenced version: 53
     D  Closing unreferenced version: 51
     D  Closing unreferenced version: 52
     D  Closing unreferenced version: 51
     D  Updating Realm version: VersionId(version=56) -> VersionId(version=57)
     D  Updating Realm version: VersionId(version=57) -> VersionId(version=57)
     E  Error:  java.lang.IllegalStateException: Cannot begin the write transaction: RealmCoreException([5]: Number of active versions (9) in the Realm exceeded the limit of 8)

Could you help me find out where do I leak Entity objects in my code? I don’t really understand where could they be stored as I never return an EntryEntity from my dao, everything is mapped right after it’s returned from the write block or after executing find()

I tried for a long time to find any public method for freezing an object /manually making it unmanaged, couldn’t find any.

Realm version: 1.4.0 Platform: Android Kotlin 1.7.20

My upsert function, which is probably the culprit:

internal inline fun <reified T : RealmObject> MutableRealm.upsert(
    id: Uuid,
    crossinline create: (RealmUUID) -> T, // verified this is not evaluated unnecessarily
    crossinline update: T.() -> Unit,
): T {
    val rid = id.toRealmUUID()
    val entity = query<T>("id == $0", rid).first().find() ?: create(rid)
    return copyToRealm(entity.apply(update), UpdatePolicy.ERROR)
}

Issue Analytics

  • State:open
  • Created a year ago
  • Comments:18 (4 by maintainers)

github_iconTop GitHub Comments

2reactions
ganfracommented, Oct 25, 2022

Thanks @cmelchior for the long answer.

On our side, having the 1. and 3. would cover most of our cases, but 2. and 4. could also be useful in some scenarios.

In our project, we often got the big file-size growth as reported in #1007, we have lots of transactions and active flows in parallel. Hopefully this will fix the issue waiting to have more control over the memory management.

2reactions
ganfracommented, Oct 24, 2022

Clearly thats not acceptable, and it’s preventing us to merge our realm-kotlin migration…

Read more comments on GitHub >

github_iconTop Results From Across the Web

Dealing with Realm transactions - Stack Overflow
1 Answer 1 ... Realm's general rule-of-thumb is that you should try to minimize as many write transactions as you can. This includes...
Read more >
Class Realm - MongoDB
This method asynchronously acquires the write lock and then dispatches the continuation on the original thread the Realm was opened on. The transaction...
Read more >
Migrating Room databases | Android Developers
To ensure that the database schema is consistent across all users when column default values are declared in your earlier migration paths, do...
Read more >
Integrating Realm Database in an Android Application - Auth0
All write operations to Realm (create, update and delete) must be wrapped in write transactions. A write transaction can either be committed ...
Read more >
Realm Xamarin 2.0.0 - Amazon AWS
All changes to an object (addition, modification, and deletion) must be done within a write transaction. To share objects between threads or re-use...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found