realm-java icon indicating copy to clipboard operation
realm-java copied to clipboard

java.lang.IllegalStateException: Cannot create asynchronous query while in a write transaction in /Users/cm/Realm/realm-java/realm/realm-library/src/main/cpp/io_realm_internal_Collection.cpp line 310

Open andrey7mel opened this issue 8 years ago • 7 comments

Goal

Avoid crashes.

Expected Results

Crashes do not happen. Transactions completes successful without crashes. Also I want to know what kind of code can lead to this crash.

Actual Results

Error. StackTrace:

java.lang.IllegalStateException: Cannot create asynchronous query while in a write transaction in /Users/cm/Realm/realm-java/realm/realm-library/src/main/cpp/io_realm_internal_Collection.cpp line 310
	at io.realm.internal.Collection.nativeStartListening(Native Method)
	at io.realm.internal.Collection.addListener(Collection.java:490)
	at io.realm.internal.Collection.addListener(Collection.java:497)
	at ...

Steps & Code to Reproduce

I do not know how to reproduce it. I haven't got anything helpful after searching all the issues and in stackoverflow.com. And I also searched the crash message Cannot create asynchronous query while in a write transaction in realm source code but found nothing. It's not a new issue and I first found this crash about one month ago. I found some similar problems in I-OS Realm, but there is no information about the solution https://github.com/realm/realm-cocoa/issues/4539 https://github.com/realm/realm-cocoa/issues/4231 https://stackoverflow.com/questions/42012222/realm-why-are-notification-blocks-triggered-when-a-write-transaction-begins

Code Sample

I've read the doc and cannot find what's wrong with my code. I know how to avoid common problems like nested transactions. But this one is killing me. Our application (en.techops.brief) uses several threads and asynchronous transactions. The problem periodically occurs in different parts of the code, here are two cases and a stacktrace:

Case 1: Useq StackTrace: https://gist.github.com/andrey7mel/e121d0e856bb20d536d970a314bdf3b0 Realm 3.3.0

05-25 18:25:02.659 15486-15486/ru.techops.brief.debug E/REALM_JNI: jni: ThrowingException 8, Cannot create asynchronous query while in a write transaction in /Users/cm/Realm/realm-java/realm/realm-library/src/main/cpp/io_realm_internal_Collection.cpp line 310, .
05-25 18:25:02.659 15486-15486/ru.techops.brief.debug E/REALM_JNI: Exception has been thrown: Cannot create asynchronous query while in a write transaction in /Users/cm/Realm/realm-java/realm/realm-library/src/main/cpp/io_realm_internal_Collection.cpp line 310
05-25 18:25:02.666 15486-15486/ru.techops.brief.debug E/ChatPresenter.l()[351]: Error while getting chat events: 
java.lang.IllegalStateException: Cannot create asynchronous query while in a write transaction in /Users/cm/Realm/realm-java/realm/realm-library/src/main/cpp/io_realm_internal_Collection.cpp line 310
	at io.realm.internal.Collection.nativeStartListening(Native Method)
	at io.realm.internal.Collection.addListener(Collection.java:490)
	at io.realm.internal.Collection.addListener(Collection.java:497)
	at io.realm.RealmResults.addChangeListener(RealmResults.java:171)
	at io.realm.rx.RealmObservableFactory$6.call(RealmObservableFactory.java:153)
	at io.realm.rx.RealmObservableFactory$6.call(RealmObservableFactory.java:137)
	at rx.Observable.unsafeSubscribe(Observable.java:10256)
	at rx.internal.operators.OnSubscribeFilter.call(OnSubscribeFilter.java:45)
	at rx.internal.operators.OnSubscribeFilter.call(OnSubscribeFilter.java:30)
	at rx.Observable.unsafeSubscribe(Observable.java:10256)
	at rx.internal.operators.OnSubscribeMap.call(OnSubscribeMap.java:48)
	at rx.internal.operators.OnSubscribeMap.call(OnSubscribeMap.java:33)
	at rx.Observable.unsafeSubscribe(Observable.java:10256)
	at rx.internal.operators.OperatorSwitch$SwitchSubscriber.onNext(OperatorSwitch.java:155)
	at rx.internal.operators.OperatorSwitch$SwitchSubscriber.onNext(OperatorSwitch.java:77)
	at rx.internal.operators.OnSubscribeMap$MapSubscriber.onNext(OnSubscribeMap.java:77)
	at rx.internal.operators.OperatorCast$CastSubscriber.onNext(OperatorCast.java:69)
	at rx.internal.operators.OnSubscribeFilter$FilterSubscriber.onNext(OnSubscribeFilter.java:76)
	at rx.internal.operators.OnSubscribeFilter$FilterSubscriber.onNext(OnSubscribeFilter.java:76)
	at io.realm.rx.RealmObservableFactory$10$1.onChange(RealmObservableFactory.java:293)
	at io.realm.rx.RealmObservableFactory$10$1.onChange(RealmObservableFactory.java:289)
	at io.realm.ProxyState$RealmChangeListenerWrapper.onChange(ProxyState.java:46)
	at io.realm.internal.OsObject$ObjectObserverPair.onChange(OsObject.java:69)
	at io.realm.internal.OsObject$Callback.onCalled(OsObject.java:88)
	at io.realm.internal.OsObject$Callback.onCalled(OsObject.java:73)
	at io.realm.internal.ObserverPairList.foreach(ObserverPairList.java:108)
	at io.realm.internal.OsObject.notifyChangeListeners(OsObject.java:236)
	at io.realm.internal.SharedRealm.nativeBeginTransaction(Native Method)
	at io.realm.internal.SharedRealm.beginTransaction(SharedRealm.java:262)
	at io.realm.BaseRealm.beginTransaction(BaseRealm.java:348)
	at io.realm.BaseRealm.beginTransaction(BaseRealm.java:343)
	at io.realm.Realm.beginTransaction(Realm.java:131)
	at io.realm.Realm.executeTransaction(Realm.java:1441)
	at ru.techops.brief.model.database.DataBase.executeTransaction(DataBase.java:1720)
	at ru.techops.brief.model.database.DataBase.lambda$null$1(DataBase.java:149)
	at ru.techops.brief.model.database.DataBase$$Lambda$160.call(Unknown Source)
	at rx.internal.operators.OnSubscribeCreate.call(OnSubscribeCreate.java:72)
	at rx.internal.operators.OnSubscribeCreate.call(OnSubscribeCreate.java:32)
	at rx.Observable.unsafeSubscribe(Observable.java:10256)
	at rx.internal.operators.OnSubscribeConcatMap$ConcatMapSubscriber.drain(OnSubscribeConcatMap.java:286)
	at rx.internal.operators.OnSubscribeConcatMap$ConcatMapSubscriber.onNext(OnSubscribeConcatMap.java:144)
	at rx.internal.operators.OperatorObserveOn$ObserveOnSubscriber.call(OperatorObserveOn.java:224)
	at rx.android.schedulers.LooperScheduler$ScheduledAction.run(LooperScheduler.java:107)
	at android.os.Handler.handleCallback(Handler.java:751)
	at android.os.Handler.dispatchMessage(Handler.java:95)
	at android.os.Looper.loop(Looper.java:154)
	at android.app.ActivityThread.main(ActivityThread.java:6688)
	at java.lang.reflect.Method.invoke(Native Method)
	at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1468)
	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1358)

Code transaction:

// ru.techops.brief.model.database.DataBase.lambda$null$1(DataBase.java:149)
saveUseqSubject
                .onBackpressureLatest()
                .concatMap(useq -> Observable.create(emitter -> {
                    executeTransaction(realm -> {
                        Useq useqObj = realm.where(Useq.class).findFirst();
                        if (useqObj == null) {
                            useqObj = new Useq();
                        }
                        useqObj.setUseq(useq);
                        realm.insertOrUpdate(useqObj);
                        emitter.onNext(useq);
                    });
                }, Emitter.BackpressureMode.BUFFER))
                .subscribe(useq1 -> Timber.v("Saved to database Useq=" + useq1),
                        e -> Timber.e(e, "Error while saving useq to Realm"));

//ru.techops.brief.model.database.DataBase.executeTransaction(DataBase.java:1720)
private void executeTransaction(Realm.Transaction transaction) {
   try (Realm realm = getRealm()) {
       realm.executeTransaction(transaction);
   }
}
 

Case 2: SaveEvents Stacktrace: https://gist.github.com/andrey7mel/10e6b54e741d99d08bd2f6174cece094 Realm 3.1.4

Non-fatal Exception: java.lang.IllegalStateException: Cannot create asynchronous query while in a write transaction in /home/cc/repo/realm/release/realm/realm-library/src/main/cpp/io_realm_internal_Collection.cpp line 310
   at io.realm.internal.Collection.nativeStartListening(Collection.java)
   at io.realm.internal.Collection.addListener(Collection.java:490)
   at io.realm.internal.Collection.addListener(Collection.java:497)
   at io.realm.RealmResults.addChangeListener(RealmResults.java:136)
   at io.realm.rx.RealmObservableFactory$6.call(RealmObservableFactory.java:153)
   at io.realm.rx.RealmObservableFactory$6.call(RealmObservableFactory.java:137)
   at rx.Observable.unsafeSubscribe(Observable.java:10346)
   at rx.internal.operators.OnSubscribeFilter.call(OnSubscribeFilter.java:45)
   at rx.internal.operators.OnSubscribeFilter.call(OnSubscribeFilter.java:30)
   at rx.Observable.unsafeSubscribe(Observable.java:10346)
   at rx.internal.operators.OnSubscribeMap.call(OnSubscribeMap.java:48)
   at rx.internal.operators.OnSubscribeMap.call(OnSubscribeMap.java:33)
   at rx.Observable.unsafeSubscribe(Observable.java:10346)
   at rx.internal.operators.OperatorSwitch$SwitchSubscriber.onNext(OperatorSwitch.java:155)
   at rx.internal.operators.OperatorSwitch$SwitchSubscriber.onNext(OperatorSwitch.java:77)
   at rx.internal.operators.OnSubscribeMap$MapSubscriber.onNext(OnSubscribeMap.java:77)
   at rx.internal.operators.OperatorCast$CastSubscriber.onNext(OperatorCast.java:69)
   at rx.internal.operators.OnSubscribeFilter$FilterSubscriber.onNext(OnSubscribeFilter.java:76)
   at rx.internal.operators.OnSubscribeFilter$FilterSubscriber.onNext(OnSubscribeFilter.java:76)
   at io.realm.rx.RealmObservableFactory$10$1.onChange(RealmObservableFactory.java:293)
   at io.realm.rx.RealmObservableFactory$10$1.onChange(RealmObservableFactory.java:289)
   at io.realm.ProxyState$RealmChangeListenerWrapper.onChange(ProxyState.java:46)
   at io.realm.internal.OsObject$ObjectObserverPair.onChange(OsObject.java:67)
   at io.realm.internal.OsObject$Callback.onCalled(OsObject.java:86)
   at io.realm.internal.OsObject$Callback.onCalled(OsObject.java:71)
   at io.realm.internal.ObserverPairList.foreach(ObserverPairList.java:108)
   at io.realm.internal.OsObject.notifyChangeListeners(OsObject.java:149)
   at io.realm.internal.SharedRealm.nativeBeginTransaction(SharedRealm.java)
   at io.realm.internal.SharedRealm.beginTransaction(SharedRealm.java:246)
   at io.realm.BaseRealm.beginTransaction(BaseRealm.java:309)
   at io.realm.Realm.beginTransaction(Realm.java:128)
   at io.realm.Realm.executeTransaction(Realm.java:1408)
   at ru.techops.brief.model.database.DataBase.executeTransaction(DataBase.java:1729)
   at ru.techops.brief.model.database.DataBase.saveEventsSync(DataBase.java:636)
   at ru.techops.brief.model.Model.lambda$saveEventsSync$85(Model.java:888)
   at ru.techops.brief.model.Model$$Lambda$70.call(Unknown Source)
   at rx.internal.operators.OnSubscribeMap$MapSubscriber.onNext(OnSubscribeMap.java:69)
   at rx.internal.util.ScalarSynchronousObservable$WeakSingleProducer.request(ScalarSynchronousObservable.java:276)
   at rx.internal.producers.ProducerArbiter.setProducer(ProducerArbiter.java:126)
   at rx.internal.operators.OnSubscribeConcatMap$ConcatMapInnerSubscriber.setProducer(OnSubscribeConcatMap.java:329)
   at rx.internal.operators.OnSubscribeMap$MapSubscriber.setProducer(OnSubscribeMap.java:102)
   at rx.internal.util.ScalarSynchronousObservable$JustOnSubscribe.call(ScalarSynchronousObservable.java:138)
   at rx.internal.util.ScalarSynchronousObservable$JustOnSubscribe.call(ScalarSynchronousObservable.java:129)
   at rx.Observable.unsafeSubscribe(Observable.java:10346)
   at rx.internal.operators.OnSubscribeMap.call(OnSubscribeMap.java:48)
   at rx.internal.operators.OnSubscribeMap.call(OnSubscribeMap.java:33)
   at rx.Observable.unsafeSubscribe(Observable.java:10346)
   at rx.internal.operators.OnSubscribeConcatMap$ConcatMapSubscriber.drain(OnSubscribeConcatMap.java:286)
   at rx.internal.operators.OnSubscribeConcatMap$ConcatMapSubscriber.innerCompleted(OnSubscribeConcatMap.java:209)
   at rx.internal.operators.OnSubscribeConcatMap$ConcatMapInnerSubscriber.onCompleted(OnSubscribeConcatMap.java:345)
   at rx.observers.SerializedObserver.onCompleted(SerializedObserver.java:176)
   at rx.observers.SerializedSubscriber.onCompleted(SerializedSubscriber.java:64)
   at rx.internal.operators.OnSubscribeConcatMap$ConcatMapSubscriber.drain(OnSubscribeConcatMap.java:246)
   at rx.internal.operators.OnSubscribeConcatMap$ConcatMapSubscriber.innerCompleted(OnSubscribeConcatMap.java:209)
   at rx.internal.operators.OnSubscribeConcatMap$ConcatMapInnerSubscriber.onCompleted(OnSubscribeConcatMap.java:345)
   at rx.observers.SerializedObserver.onCompleted(SerializedObserver.java:176)
   at rx.observers.SerializedSubscriber.onCompleted(SerializedSubscriber.java:64)
   at rx.internal.operators.OnSubscribeConcatMap$ConcatMapSubscriber.drain(OnSubscribeConcatMap.java:246)
   at rx.internal.operators.OnSubscribeConcatMap$ConcatMapSubscriber.innerCompleted(OnSubscribeConcatMap.java:209)
   at rx.internal.operators.OnSubscribeConcatMap$ConcatMapInnerSubscriber.onCompleted(OnSubscribeConcatMap.java:345)
   at rx.internal.operators.OperatorMerge$MergeSubscriber.emitLoop(OperatorMerge.java:656)
   at rx.internal.operators.OperatorMerge$MergeSubscriber.emit(OperatorMerge.java:568)
   at rx.internal.operators.OperatorMerge$MergeSubscriber.onCompleted(OperatorMerge.java:281)
   at rx.internal.operators.OnSubscribeMap$MapSubscriber.onCompleted(OnSubscribeMap.java:97)
   at rx.internal.operators.OperatorOnErrorResumeNextViaFunction$4.onCompleted(OperatorOnErrorResumeNextViaFunction.java:101)
   at rx.internal.operators.OperatorObserveOn$ObserveOnSubscriber.checkTerminated(OperatorObserveOn.java:281)
   at rx.internal.operators.OperatorObserveOn$ObserveOnSubscriber.call(OperatorObserveOn.java:216)
   at rx.android.schedulers.LooperScheduler$ScheduledAction.run(LooperScheduler.java:107)
   at android.os.Handler.handleCallback(Handler.java:815)
   at android.os.Handler.dispatchMessage(Handler.java:104)
   at android.os.Looper.loop(Looper.java:207)
   at android.app.ActivityThread.main(ActivityThread.java:5896)
   at java.lang.reflect.Method.invoke(Method.java)
   at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:948)
   at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:809)

Code:

// ru.techops.brief.model.Model.lambda$saveEventsSync$85(Model.java:888)
 public Completable saveEventsSync(List<WsUpdateEventNew> updates) {
        List<Event> events = new ArrayList<>();
        for (WsUpdateEventNew update : updates) {
            events.add(update.getEvent());
        }
        return Observable.from(events)
                .concatMap(event -> {
                    Observable<Object> res = Observable.empty();
                    User sender = dataBase.getUserSyncFromRealm(event.getSenderId());
                    if (sender == null) {
                        res = res.concatWith(getUserInfoFromServer(event.getSenderId()));
                    }
                    Peer fwdSenderPeer = event.getFwdSenderPeer();
                    if (fwdSenderPeer != null) {
                        User fwdSender = dataBase.getUserSyncFromRealm(fwdSenderPeer.getPeerId());
                        if (fwdSender == null) {
                            res = res.concatWith(getUserInfoFromServer(fwdSenderPeer.getPeerId()));
                        }
                    }
                    Peer peer = event.getPeer();
                    Const.NotifyMode notifyMode = dataBase.getPeerNotifyMode(peer);
                    if (notifyMode == Const.NotifyMode.NOTIFY_UNKNOWN) {
                        switch (peer.getType()) {
                            case PeerType.USER:
                                res = res.concatWith(getUserInfoFromServer(event.getPeer().getPeerId()));
                                break;
                            case PeerType.GROUP:
                                res = res.concatWith(getGroupInfoFromServer(event.getPeer().getPeerId()));
                                break;
                        }
                    }
                    if (event.getReplyToId() != 0) {
                        res = res.concatWith(loadEventIfNeed(event.getPeer(), event.getReplyToId()).ignoreElements());
                    }
                    return res;
                })
                .concatWith(Observable.just(events)
                        .map(e -> {
                        // (Model:888)       
                            dataBase.saveEventsSync(e);
                            return e;
                        }))
                .toCompletable();
    }
 
 
//ru.techops.brief.model.database.DataBase.saveEventsSync(DataBase.java:636)
@WorkerThread
public void saveEventsSync(List<Event> events) {
    executeTransaction(realm -> {
       for (Event event : events) {
           saveEventToRealm(event, realm);
       }
   });
}


// ru.techops.brief.model.database.DataBase.executeTransaction(DataBase.java:1729)
    private void executeTransaction(Realm.Transaction transaction) {
        try (Realm realm = getRealm()) {
            realm.executeTransaction(transaction);
        }
    }

Version of Realm and tooling

Realm version(s): 3.1.4 - 3.3.0

Realm sync feature enabled: no

Android Studio version: 2.3.2

Which Android version and device: Android 5 - 6 - 7, many devices image

andrey7mel avatar May 26 '17 16:05 andrey7mel

Well the error technically means that you have synchronous Realm write on the UI thread, and at some point it tries to call asObservable in a switchMap operation on an async query results, and it breaks.

But I'm not sure exactly where it happens, except that beginTransaction wants to call the change listeners, and that's when the crash is triggered.

I am not a Realm person, by the way, so this is by no means the official support response.

Zhuinden avatar May 26 '17 21:05 Zhuinden

This looks weird to me. I've asked some of our ObjectStore experts to have a look. Unfortunately, we have holidays in the US, Denmark, and the UK, on Monday, so this may not get attention until mid next week.

bmeike avatar May 26 '17 23:05 bmeike

Previously it just silently converted async query to sync query, it didn't throw exception :D

Zhuinden avatar May 27 '17 01:05 Zhuinden

It is a known restriction from Object Store that Results.addChangeListener() cannot be called in the transaction now. This needs to be documented in the javadoc.

beeender avatar May 28 '17 06:05 beeender

@beeender Adding that restriction to findAllAsync() would probably be more appropriate? @andrey7mel Can you also post the saveEventToRealm(event, realm); method in your code above?

cmelchior avatar May 30 '17 14:05 cmelchior

@cmelchior due to the nature of how Object Store running the queries, even findAll() will be put into background thread if it is possible. That means calling findAll().addChangeListener() inside a transaction is not allowed as well.

beeender avatar May 30 '17 14:05 beeender

@cmelchior

private void saveEventToRealm(Event event, Realm bgRealm) {
        User sender = bgRealm.where(User.class).equalTo("id", event.getSenderId()).findFirst();
        if (sender != null) {
            event.setSender(sender);
        }
        if (event.getFwdSenderPeer() != null) {
            User fwdUser = bgRealm.where(User.class).equalTo("id", event.getFwdSenderPeer().getPeerId()).findFirst();
            if (fwdUser != null) {
                event.setFwdSender(fwdUser);
            }
        }

        ServiceAction action = event.getAction();
        if (action != null && action.getUserId() != 0) {
            User actionUser = bgRealm.where(User.class).equalTo("id", action.getUserId()).findFirst();
            if (actionUser != null) {
                action.setUser(actionUser);
                event.setAction(action);
            }
        }

        if (event.getReplyToId() != 0) {
            setReplyEvent(event, bgRealm);
        }

        event.setState(Event.EventState.SENT);

        FileBrief file = event.getFile();
        if (file != null && event.getFile() != null) {
            file.setId(event.getFile().getId());
        }

        Event eventRealm = bgRealm.copyToRealmOrUpdate(event);

        Chat chat = bgRealm.where(Chat.class)
                .equalTo("peer.uniquePeerId", event.getPeer().getUniquePeerId())
                .findFirst();

        if (chat != null) {
            if (event.getSenderId() != getSelfUserId() && event.getId() > chat.getReadEventId()) {
                int unreadCount = chat.getUnreadCountSafe();
                chat.setUnreadCount(unreadCount + 1);
            }
            Timber.d("Chat " + chat.getChatName() + " , set last  event - " + event.getText());
            chat.setLastEvent(eventRealm);

        } else {
            Timber.i("Chat == null in saveEvent => create new chat");
            if (event.getPeer().getType().equals(PeerType.USER)) {
                User user = bgRealm.where(User.class).equalTo("id", event.getPeer().getPeerId()).findFirst();
                if (user != null) {
                    chat = new Chat(event.getPeer(), eventRealm, user);
                    bgRealm.insertOrUpdate(chat);
                } else {
                    Timber.e("Chat == null, user == null, can't create Chat");
                }
            } else {
                Group group = bgRealm.where(Group.class).equalTo("id", event.getPeer().getPeerId()).findFirst();
                if (group != null) {
                    chat = new Chat(event.getPeer(), eventRealm, group);
                    bgRealm.insertOrUpdate(chat);
                } else {
                    Timber.e("Chat == null, group == null, can't create Chat");
                }
            }

            if (chat != null) {
                if (event.getAction() != null) {
                    if (ServiceAction.ACTION_GROUP_CREATED.equals(event.getAction().getType())
                            && event.getSenderId() != getSelfUserId()) {
                        // если группу создал не я
                        chat.setUnreadCount(1);
                    }
                    if (!ServiceAction.ACTION_GROUP_CREATED.equals(event.getAction().getType())) {
                        chat.setUnreadCount(1);
                    }
                }
                bgRealm.insertOrUpdate(chat);
            } else {
                Timber.e("Chat == null or group == null, can't create Chat in saveEventToRealm");
            }
        }
    }

andrey7mel avatar May 30 '17 15:05 andrey7mel