realm / realm-js

Realm is a mobile database: an alternative to SQLite & key-value stores
https://realm.io
Apache License 2.0
5.81k stars 577 forks source link

The Realm is already in a write transaction #6834

Open feidaZhang opened 3 months ago

feidaZhang commented 3 months ago

How frequently does the bug occur?

Sometimes

Description

context

We found the issue in realm 10.15.0 and react-native 0.66.3 one year ago. After we upgrade realm to 12.6.2 and react-native to 0.72.12. We still can get these exceptions from logs in production. However, we can not repro it on our side. It's too bad. We call the following method in a loop. I am not sure if it causes the issue.

public static saveTransaction(entity) {
    try {
      RealmManager.getRealmInstance().write(() => {
        RealmManager.getRealmInstance().create('Transaction', entity, UpdateMode.All);
      });
      return true;
    } catch (exception) {
      return false;
    }
  }

errors on iOS 17.5.1

The exceptions happen frequently to the user, who is on iOS 17.5.1. realm_error_ios

image

errors on other users

issue: The Realm is already in a write transaction Also, there are a few error logs and they are all instantaneous in other Android and iOS users. image

Stacktrace & log output

No response

Can you reproduce the bug?

No

Reproduction Steps

No response

Version

12.6.2

What services are you using?

Local Database only

Are you using encryption?

No

Platform OS and version(s)

iOS 17.5.1

Build environment

Which debugger for React Native: ..

Cocoapods version

No response

sync-by-unito[bot] commented 3 months ago

➤ PM Bot commented:

Jira ticket: RJS-2882

kneth commented 3 months ago

It is difficult to tell as the functionality of RealmManager.getRealmInstance() is unknown. My guess is that the method returns a cached Realm instance which is already in a write transaction.

Instead of write(), you can use manual transactions: Realm.isInTransaction(), Realm.beginTransaction(), Realm.commitTransaction(), and Realm.cancelTransaction().

feidaZhang commented 3 months ago

Yes. We have a cached realm singleton globally. However, this instance will not be modified after it is created. All our business operations run on the JS thread of RN, why does this still cause this problem? @kneth and, may i know the difference between write() and beginTransaction(), commitTransaction() ? because we always use the write().

let realm: Realm;
export default class RealmManager {
  static getRealmInstance = () => {
    return realm;
  };
}
kneth commented 3 months ago

You could try to write your method to:


public static saveTransaction(entity) {
    const realm = RealmManager.getRealmInstance();
    let inTransaction = realm.isInTransaction;
    if (!inTransaction) {
      realm.beginTransaction();
    }
    realm.create('Transaction', entity, UpdateMode.All);
    if (!inTransaction) {
      realm.commitTransaction();
    }
  }
feidaZhang commented 3 months ago

You could try to write your method to:

public static saveTransaction(entity) {
    const realm = RealmManager.getRealmInstance();
    let inTransaction = realm.isInTransaction;
    if (!inTransaction) {
      realm.beginTransaction();
    }
    realm.create('Transaction', entity, UpdateMode.All);
    if (!inTransaction) {
      realm.commitTransaction();
    }
  }

Do you mean we are able to reuse the current transaction?

feidaZhang commented 3 months ago

and, Is this error related to memory collecting failure? We got the error logs that were thrown from write(). memory

kneth commented 3 months ago

mmap() failed: Cannot allocate memory ... - it sounds like you have a transaction which is too large to handle for the database. Can you split it multiple transactions?