realm / realm-dart

Realm is a mobile database: a replacement for SQLite & ORMs.
Apache License 2.0
770 stars 86 forks source link

Error: Wrong transactional state on update #926

Closed wilsleygermano closed 1 year ago

wilsleygermano commented 2 years ago

What happened?

I'm developing a flutter app with realm as a (local-only) database. My project is based on the OOD principles, so to get the "dependency inversion principle" right, I'm using Flutter Modular. That said, when I try to update an object, I got this error:

Error setting property [RealmObject.propertyX] Error: RealmException: Error code: 5 . Message: Wrong transactional state (no active transaction, wrong type of transaction, or transaction already in progress).

Every other method (write, delete, read) is working well. The issue is only with the update.

If I ignore the dependency inversion principle, i.e. create an instance of my realm object on the page, then I can update the property. But I can't ignore that principle in this project.

Just in case, this is my flutter doctor output:

Doctor summary (to see all details, run flutter doctor -v): [✓] Flutter (Channel stable, 3.3.0, on macOS 12.6 21G115 darwin-arm, locale en-BR) [✓] Android toolchain - develop for Android devices (Android SDK version 32.1.0-rc1) [✓] Xcode - develop for iOS and macOS (Xcode 14.0.1) [✓] Chrome - develop for the web [✓] Android Studio (version 2021.1) [✓] VS Code (version 1.71.2) [✓] Connected device (3 available) [✓] HTTP Host Availability • No issues found!

Repro steps

class FirstPage extends StatefulWidget {
  const FirstPage({Key? key, required this.title}) : super(key: key);

  final String title;

  @override
  State<FirstPage> createState() => _FirstPageState();
}

class _FirstPageState extends State<FirstPage> {
  DatabaseClient realm = Modular.get<DatabaseClient>();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: SingleChildScrollView(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.spaceEvenly,
            children: <Widget>[
              TextButton(
                  onPressed: () {
                   final project =
                        realm.readOneProject(projectUID: "X");
                    realm.update(
                        objectToUpdate: project.data!.first.propertyX = "80");

                  },
                  child: Text("RUN")
            ],
          ),
        ),
      ),
    );
  }
}

Version

Flutter 3.3.0 / Dart 2.18.0

What Realm SDK flavor are you using?

Local Database only

What type of application is this?

Flutter Application

Client OS and version

macOS 12.6

Code snippets

class DatabaseClientImpl implements DatabaseClient {
  late Realm database;

  DatabaseClientImpl([Realm? client]) {
    if (client == null) {
      database = Realm(Configuration.local([RealmEcoForm.schema]));
      debugPrint("Is realm closed: ${database.isClosed}");
    } else {
      database = client;
    }
  }

  @override
  DatabaseResponse<Object, String> delete({required String projectUID}) {
    try {
      final RealmEcoForm objectToDelete =
          database.find<RealmEcoForm>(projectUID)!;
      database.delete(objectToDelete);

      return DatabaseResponse();
    } on RealmError catch (e) {
      return DatabaseResponse(error: e.message);
    }
  }

  @override
  DatabaseResponse<Object, String> write({required Object object}) {
    try {
      database.write<RealmEcoForm>(() => database.add(object as RealmEcoForm));
      return DatabaseResponse(data: object);
    } on RealmError catch (e) {
      return DatabaseResponse(error: e.message);
    }
  }

  @override
  DatabaseResponse<Object, String> update({
    required Object objectToUpdate,
  }) {
    try {
      debugPrint("Is realm closed: ${database.isClosed}");
      database.write(() => objectToUpdate);
      return DatabaseResponse(data: objectToUpdate);
    } on RealmError catch (e) {
      debugPrint(e.message);
      return DatabaseResponse(error: e.message);
    }
  }

  @override
  DatabaseResponse<RealmResults<RealmEcoForm>, String> readAllProjects() {
    try {
      final projects = database.all<RealmEcoForm>();
      return DatabaseResponse(data: projects);
    } on RealmError catch (e) {
      return DatabaseResponse(error: e.message);
    }
  }

  @override
  DatabaseResponse<RealmResults<RealmEcoForm>, String> readOneProject(
      {required String projectUID}) {
    try {
      final RealmResults<RealmEcoForm> response =
          database.query<RealmEcoForm>('projectUID == "$projectUID"');
      return DatabaseResponse(data: response);
    } on RealmError catch (e) {
      debugPrint(e.message);
      return DatabaseResponse(error: e.message);
    }
  }

  @override
  DatabaseResponse<dynamic, String> deleteEverything() {
    try {
      database.deleteAll<RealmEcoForm>();
      return DatabaseResponse();
    } on RealmError catch (e) {
      return DatabaseResponse(error: e.message);
    }
  }
}

Stacktrace of the exception/crash you're getting

When the exception was thrown, this was the stack
#0      RealmCoreAccessor.set
#1      RealmObject.set
#2      RealmEcoForm.herbCover50=
#3      _FirstPageState.build.<anonymous closure>
#4      _InkResponseState.handleTap
#5      GestureRecognizer.invokeCallback
#6      TapGestureRecognizer.handleTapUp
#7      BaseTapGestureRecognizer._checkUp
#8      BaseTapGestureRecognizer.handlePrimaryPointer
#9      PrimaryPointerGestureRecognizer.handleEvent
#10     PointerRouter._dispatch
#11     PointerRouter._dispatchEventToRoutes.<anonymous closure>
#12     _LinkedHashMapMixin.forEach (dart:collection-patch/compact_hash.dart:617:13)
#13     PointerRouter._dispatchEventToRoutes
#14     PointerRouter.route
#15     GestureBinding.handleEvent
#16     GestureBinding.dispatchEvent
#17     RendererBinding.dispatchEvent
#18     GestureBinding._handlePointerEventImmediately
#19     GestureBinding.handlePointerEvent
#20     GestureBinding._flushPointerEventQueue
#21     GestureBinding._handlePointerDataPacket
#22     _invoke1 (dart:ui/hooks.dart:167:13)
#23     PlatformDispatcher._dispatchPointerDataPacket (dart:ui/platform_dispatcher.dart:341:7)
#24     _dispatchPointerDataPacket (dart:ui/hooks.dart:94:31)
Handler: "onTap"
Recognizer: TapGestureRecognizer#733cd
    debugOwner: GestureDetector
    state: possible
    won arena
    finalPosition: Offset(191.7, 482.0)
    finalLocalPosition: Offset(98.7, 26.5)
    button: 1
    sent tap down
════════════════════════════════════════════════════════════════════════════════

Relevant log output

Restarted application in 708ms.
flutter: AppModule started!
flutter: -- SigninModule INITIALIZED
flutter: Is realm closed: false

════════ Exception caught by gesture ═══════════════════════════════════════════
The following RealmException was thrown while handling a gesture:
Error setting property RealmEcoForm.herbCover50 Error: RealmException: Error code: 5 . Message: Wrong transactional state (no active transaction, wrong type of transaction, or transaction already in progress)
Exception backtrace:
0   realm_dart                          0x0000000109c39db4 _ZN5realm10LogicErrorC1ENS0_9ErrorKindE + 52
1   realm_dart                          0x0000000109ede43c _ZN5realm4Node16do_copy_on_writeEm + 404
2   realm_dart                          0x0000000109e69f74 _ZN5realm16ArrayStringShort3setEmNS_10StringDataE + 508
3   realm_dart                          0x0000000109edeafc _ZN5realm3Obj3setINS_10StringDataEEERS0_NS_6ColKeyET_b + 608
4   realm_dart                          0x0000000109ee7800 _ZN5realm3Obj7set_anyENS_6ColKeyENS_5MixedEb + 184
5   realm_dart                          0x0000000109c437b8 realm_set_values + 340
6   realm_dart                          0x0000000109c43658 realm_set_value + 36
7   ???                                 0x000000011a586608 0x0 + 4736968200
8   ???                                 0x00000001249c4fb8 0x0 + 4909191096
9   ???                                 0x00000001249c49e8 0x0 + 4909189608
10  ???                                 0x00000001249c45e0 0x0 + 4909188576
11  ???                                 0x000000012691091c 0x0 + 4942006556
12  ???                                 0x00000001249c1890 0x0 + 4909176976
13  ???                                 0x0000000126178814 0x0 + 4934043668
14  ???                                 0x00000001249c1688 0x0 + 4909176456
15  ???                                 0x00000001249c0bf0 0x0 + 4909173744
16  ???                                 0x00000001249c0268 0x0 + 4909171304
17  ???                                 0x00000001249bff04 0x0 + 4909170436
18  ???                                 0x00000001249b3d24 0x0 + 4909120804
19  ???                                 0x00000001249ae2e0 0x0 + 4909097696
20  ???                                 0x00000001249adee0 0x0 + 4909096672
21  ???                                 0x0000000124988034 0x0 + 4908941364
22  ???                                 0x00000001249aca64 0x0 + 4909091428
23  ???                                 0x00000001249a1b04 0x0 + 4909046532
24  ???                                 0x0000000124983964 0x0 + 4908923236
25  ???                                 0x0000000124982b60 0x0 + 4908919648
26  ???                                 0x0000000124982398 0x0 + 4908917656
27  ???                                 0x000000012bbe3518 0x0 + 5028853016
28  ???                                 0x000000012bbe3208 0x0 + 5028852232
29  ???                                 0x0000000124e91dcc 0x0 + 4914224588
30  ???                                 0x000000012bbe3098 0x0 + 5028851864
31  ???                                 0x000000012bbe2c4c 0x0 + 5028850764
32  ???                                 0x0000000124981ddc 0x0 + 4908916188
33  ???                                 0x000000012bbe29fc 0x0 + 5028850172
34  ???                                 0x000000012bbe1978 0x0 + 5028845944
35  ???                                 0x000000012bbe11b0 0x0 + 5028843952
36  ???                                 0x000000012bbde55c 0x0 + 5028832604
37  ???                                 0x000000012bbac71c 0x0 + 5028628252
38  ???                                 0x000000012bbcd274 0x0 + 5028762228
39  ???                                 0x000000012bbcd088 0x0 + 5028761736
40  ???                                 0x000000011d031e2c 0x0 + 4781710892
41  ???                                 0x000000012bbc59dc 0x0 + 5028731356
42  ???                                 0x000000012bbc5828 0x0 + 5028730920
43  ???                                 0x000000011a582ed8 0x0 + 4736954072
44  Flutter                             0x000000010ea36ed0 _ZN4dart9DartEntry10InvokeCodeERKNS_4CodeEmRKNS_5ArrayES6_PNS_6ThreadE + 312
45  Flutter                             0x000000010ea36d3c _ZN4dart9DartEntry14InvokeFunctionERKNS_8FunctionERKNS_5ArrayES6_m + 308
46  Flutter                             0x000000010eac37ac _ZNK4dart7Library6InvokeERKNS_6StringERKNS_5ArrayES6_bb + 564
47  Flutter                             0x000000010ed1bf70 Dart_Invoke + 1480
48  Flutter                             0x000000010e95d688 _ZN7flutter6Window25DispatchPointerDataPacketERKNS_17PointerDataPacketE + 200
49  Flutter                             0x000000010e9eb714 _ZN7flutter17RuntimeController25DispatchPointerDataPacketERKNS_17PointerDataPacketE + 88
50  Flutter                             0x000000010e83b6bc _ZN7flutter28DefaultPointerDataDispatcher14DispatchPacketENSt3__110unique_ptrINS_17PointerDataPacketENS1_14default_deleteIS3_EEEEy + 112
51  Flutter                             0x000000010e83b7fc _ZN7flutter27SmoothPointerDataDispatcher21DispatchPendingPacketEv + 48
52  Flutter                             0x000000010e753298 _ZN3fml15MessageLoopImpl10FlushTasksENS_9FlushTypeE + 152
53  Flutter                             0x000000010e759b50 _ZN3fml17MessageLoopDarwin11OnTimerFireEP16__CFRunLoopTimerPS0_ + 32
54  CoreFoundation                      0x0000000180371720 __CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__ + 28
55  CoreFoundation                      0x00000001803713dc __CFRunLoopDoTimer + 868
56  CoreFoundation                      0x0000000180370ac8 __CFRunLoopDoTimers + 284
57  CoreFoundation                      0x000000018036b28c __CFRunLoopRun + 1816
58  CoreFoundation                      0x000000018036a75c CFRunLoopRunSpecific + 584
59  Flutter                             0x000000010e759c80 _ZN3fml17MessageLoopDarwin3RunEv + 88
60  Flutter                             0x000000010e7531b0 _ZN3fml15MessageLoopImpl5DoRunEv + 40
61  Flutter                             0x000000010e75895c _ZNSt3__1L14__thread_proxyINS_5tupleIJNS_10unique_ptrINS_15__thread_structENS_14default_deleteIS3_EEEEZN3fml6ThreadC1ERKNS_8functionIFvRKNS8_12ThreadConfigEEEESC_E3$_0EEEEEPvSJ_ + 208
62  libsystem_pthread.dylib             0x00000001ae5626c8 _pthread_start + 116
63  libsystem_pthread.dylib             0x00000001ae55d910 thread_start + 8
desistefanova commented 2 years ago

Hi @wilsleygermano, This error appears, because you try to edit a RealmObject property outside a write block. You have to set project.data!.first.propertyX = "80 inside your database.write. Probably you can pass the objectToUpdate and a function (objectToUpdate)=> objectToUpdate.propertyX = "80 to your DatabaseClient update method and to call this function inside database.write block. This is just one suggestion, you may find some other way to do it. Best regards!

github-actions[bot] commented 1 year ago

This issue has been automatically closed because there has been no response to our request for more information from the original author. With only the information that is currently in the issue, we don't have enough information to take action. Please reach out if you have or find the answers we need so that we can investigate further.