Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Assertion failed: !realm.is_in_transaction() #6659

Closed
Cheezzhead opened this issue May 23, 2023 · 7 comments
Closed

Assertion failed: !realm.is_in_transaction() #6659

Cheezzhead opened this issue May 23, 2023 · 7 comments

Comments

@Cheezzhead
Copy link

Cheezzhead commented May 23, 2023

SDK and version

SDK : Swift (SPM)
Version: 10.39.1 (RealmDatabase: 13.10.1)

Observations

  • How frequent do the crash occur? Randomly, indeterminate
  • Does it happen in production or during dev/test? dev, but assumedly also in production
  • Can the crash be reproduced by you? Not consistently
  • Can you provide instructions for how we can reproduce it? No

Crash log / stacktrace

/Users/xxx/Library/Developer/Xcode/DerivedData/xxx/checkouts/realm-core/src/realm/object-store/impl/realm_coordinator.cpp:1109: [realm-core-13.10.1] Assertion failed: !realm.is_in_transaction()
0   DevApp                          0x00000001052f48e8 _ZN5realm4utilL18terminate_internalERNSt3__118basic_stringstreamIcNS1_11char_traitsIcEENS1_9allocatorIcEEEE + 28
1   DevApp                          0x00000001052f48c8 _ZN5realm4util19terminate_with_infoEPKcS2_lS2_OSt16initializer_listINS0_9PrintableEE + 308
2   DevApp                          0x00000001052f4794 _ZN5realm4util19terminate_with_infoEPKcS2_lS2_OSt16initializer_listINS0_9PrintableEE + 0
3   DevApp                          0x0000000104d32d98 _ZN5realm5_impl16RealmCoordinator16promote_to_writeERNS_5RealmE + 88
4   DevApp                          0x0000000104e5f0ac _ZN5realm5Realm20do_begin_transactionEv + 56
5   DevApp                          0x0000000104e5eca4 _ZN5realm5Realm10run_writesEv + 204
6   DevApp                          0x0000000104d4b80c _ZN5realm5Realm8Internal10run_writesERS0_ + 24
7   DevApp                          0x0000000104d4b7e8 _ZZZN5realm5_impl16RealmCoordinator25async_request_write_mutexERNS_5RealmEEN3$_9clEvENKUlvE_clEv + 28
8   DevApp                          0x0000000104d4b7c0 _ZN5realm4util14UniqueFunctionIFvvEE17call_regular_voidIZZNS_5_impl16RealmCoordinator25async_request_write_mutexERNS_5RealmEEN3$_9clEvEUlvE_EEvNSt3__117integral_constantIbLb1EEERT_ + 24
9   DevApp                          0x0000000104d4b720 _ZN5realm4util14UniqueFunctionIFvvEE12SpecificImplIZZNS_5_impl16RealmCoordinator25async_request_write_mutexERNS_5RealmEEN3$_9clEvEUlvE_E4callEv + 28
10  DevApp                          0x00000001048d6b98 _ZNK5realm4util14UniqueFunctionIFvvEEclEv + 100
11  DevApp                          0x00000001048d6ac8 ___ZN12_GLOBAL__N_114ActorScheduler6invokeEON5realm4util14UniqueFunctionIFvvEEE_block_invoke + 52
12  DevApp                          0x00000001049a16c0 $sIeyB_Ieg_TR + 16
13  DevApp                          0x00000001049c7abc $sIeg_ytIegr_TR + 20
14  DevApp                          0x0000000104a0f4dc $sytIeghr_Iegh_TR + 20
15  DevApp                          0x0000000104a0f6d8 $sScA10RealmSwiftE8doInvoke33_3EEC705E1F715BCD31B1A87960A34A90LLyyyyYbXEF + 64
16  DevApp                          0x0000000104a0f5fc $sScA10RealmSwiftE6invokeyyyycFyyYaYbcfU_TY0_ + 100
17  libswift_Concurrency.dylib          0x000000010c1f9a38 _ZN5swift34runJobInEstablishedExecutorContextEPNS_3JobE + 304
18  libswift_Concurrency.dylib          0x000000010c1fb104 _ZN12_GLOBAL__N_119ProcessOutOfLineJob7processEPN5swift3JobE + 852
19  libswift_Concurrency.dylib          0x000000010c1f9abc _ZN5swift34runJobInEstablishedExecutorContextEPNS_3JobE + 436
20  libswift_Concurrency.dylib          0x000000010c1fa704 _ZL17swift_job_runImplPN5swift3JobENS_11ExecutorRefE + 80
21  libdispatch.dylib                   0x000000010d468fac _dispatch_continuation_pop + 152
22  libdispatch.dylib                   0x000000010d468188 _dispatch_async_redirect_invoke + 904
23  libdispatch.dylib                   0x000000010d47a900 _dispatch_root_queue_drain + 440
24  libdispatch.dylib                   0x000000010d47b574 _dispatch_worker_thread2 + 248
25  libsystem_pthread.dylib             0x000000010de638c0 _pthread_wqthread + 224
26  libsystem_pthread.dylib             0x000000010de626c0 start_wqthread + 8
!!! IMPORTANT: Please report this at https://github.com/realm/realm-core/issues/new/choose2023-05-23 15:27:47.584056+0200 DevApp[25066:28904398] /Users/xxx/Library/Developer/Xcode/DerivedData/xxx/checkouts/realm-core/src/realm/object-store/impl/realm_coordinator.cpp:1109: [realm-core-13.10.1] Assertion failed: !realm.is_in_transaction()
0   DevApp                          0x00000001052f48e8 _ZN5realm4utilL18terminate_internalERNSt3__118basic_stringstreamIcNS1_11char_traitsIcEENS1_9allocatorIcEEEE + 28
1   DevApp                          0x00000001052f48c8 _ZN5realm4util19terminate_with_infoEPKcS2_lS2_OSt16initializer_listINS0_9PrintableEE + 308
2   DevApp                          0x00000001052f4794 _ZN5realm4util19terminate_with_infoEPKcS2_lS2_OSt16initializer_listINS0_9PrintableEE + 0
3   DevApp                          0x0000000104d32d98 _ZN5realm5_impl16RealmCoordinator16promote_to_writeERNS_5RealmE + 88
4   DevApp                          0x0000000104e5f0ac _ZN5realm5Realm20do_begin_transactionEv + 56
5   DevApp                          0x0000000104e5eca4 _ZN5realm5Realm10run_writesEv + 204
6   DevApp                          0x0000000104d4b80c _ZN5realm5Realm8Internal10run_writesERS0_ + 24
7   DevApp                          0x0000000104d4b7e8 _ZZZN5realm5_impl16RealmCoordinator25async_request_write_mutexERNS_5RealmEEN3$_9clEvENKUlvE_clEv + 28
8   DevApp                          0x0000000104d4b7c0 _ZN5realm4util14UniqueFunctionIFvvEE17call_regular_voidIZZNS_5_impl16RealmCoordinator25async_request_write_mutexERNS_5RealmEEN3$_9clEvEUlvE_EEvNSt3__117integral_constantIbLb1EEERT_ + 24
9   DevApp                          0x0000000104d4b720 _ZN5realm4util14UniqueFunctionIFvvEE12SpecificImplIZZNS_5_impl16RealmCoordinator25async_request_write_mutexERNS_5RealmEEN3$_9clEvEUlvE_E4callEv + 28
10  DevApp                          0x00000001048d6b98 _ZNK5realm4util14UniqueFunctionIFvvEEclEv + 100
11  DevApp                          0x00000001048d6ac8 ___ZN12_GLOBAL__N_114ActorScheduler6invokeEON5realm4util14UniqueFunctionIFvvEEE_block_invoke + 52
12  DevApp                          0x00000001049a16c0 $sIeyB_Ieg_TR + 16
13  DevApp                          0x00000001049c7abc $sIeg_ytIegr_TR + 20
14  DevApp                          0x0000000104a0f4dc $sytIeghr_Iegh_TR + 20
15  DevApp                          0x0000000104a0f6d8 $sScA10RealmSwiftE8doInvoke33_3EEC705E1F715BCD31B1A87960A34A90LLyyyyYbXEF + 64
16  DevApp                          0x0000000104a0f5fc $sScA10RealmSwiftE6invokeyyyycFyyYaYbcfU_TY0_ + 100
17  libswift_Concurrency.dylib          0x000000010c1f9a38 _ZN5swift34runJobInEstablishedExecutorContextEPNS_3JobE + 304
18  libswift_Concurrency.dylib          0x000000010c1fb104 _ZN12_GLOBAL__N_119ProcessOutOfLineJob7processEPN5swift3JobE + 852
19  libswift_Concurrency.dylib          0x000000010c1f9abc _ZN5swift34runJobInEstablishedExecutorContextEPNS_3JobE + 436
20  libswift_Concurrency.dylib          0x000000010c1fa704 _ZL17swift_job_runImplPN5swift3JobENS_11ExecutorRefE + 80
21  libdispatch.dylib                   0x000000010d468fac _dispatch_continuation_pop + 152
22  libdispatch.dylib                   0x000000010d468188 _dispatch_async_redirect_invoke + 904
23  libdispatch.dylib                   0x000000010d47a900 _dispatch_root_queue_drain + 440
24  libdispatch.dylib                   0x000000010d47b574 _dispatch_worker_thread2 + 248
25  libsystem_pthread.dylib             0x000000010de638c0 _pthread_wqthread + 224
26  libsystem_pthread.dylib             0x000000010de626c0 start_wqthread + 8
!!! IMPORTANT: Please report this at https://github.com/realm/realm-core/issues/new/choose

Steps & Code to Reproduce

It's hard to reproduce this error consistently, although I do get this (and other assertion failures) while randomly clicking through the app.

Most of the Realm-related code (specifically writing) has migrated to the new Actor-isolated realm instances, using asyncWrite(_:), however some parts may still use the older method (usually just try! Realm()). Concretely, anytime a view requires a realm object, it is retrieved using the old method (on the main actor). Is it possible that this is the cause of the issue?

@nicola-cab
Copy link
Member

Hello, thanks for reporting this. It seems to me that you tried to open a write transaction while there was another write transaction open for the same Realm.
Maybe having some code or a minimum reproducible case can help us to identify if there is something that we need to address in our code.

Also, @tgoyne do you have some insight here?
I am not sure how the new Actor-isolated implementation works.

@Cheezzhead
From core perspective it is not OK to have parts of the code that are writing using asyncWrite vs trying to open a transaction in no async code (if this is what it is happening). We don't support multiple parallel write transactions.

@tgoyne
Copy link
Member

tgoyne commented May 23, 2023

We do support mixing async and sync writes, which is one of the major sources of complexity in the feature.

@tgoyne tgoyne self-assigned this May 23, 2023
@tgoyne
Copy link
Member

tgoyne commented May 23, 2023

I've successfully reproduced this.

@tgoyne
Copy link
Member

tgoyne commented May 23, 2023

Note that the thing I've reproduced involves suspending while in a write transaction, which is not a safe thing to do. If you're using realm.beginWrite() in an async function (rather than the block version) you can place awaits between that and the call to realm.commitWrite(), but doing so violates Swift Concurrency's rules about not suspending while holding a lock and it's easy to hit deadlocks if you do so. In the next major version beginWrite() will probably be marked as nonasync, but it would be a breaking change to do it now.

@Cheezzhead
Copy link
Author

So it's hard to show some minimal producable case, because (a) the assertion errors occur at random times and (b) they don't occur in the places where transactions are happening, but rather at moments of retrieval in a view (so on the main actor). Presumably, the assertion failure occurs when async writes are occurring while objects are retrieved simultaneously?

Below is some (edited for clarity) code of how the app handles reads and writes.

extension BaseObjectProtocol {
    // Open object in the shared realm and write to it
    @RealmActor func update<Result>(_ block: (Self, Realm) throws -> Result) async throws -> Result
    {
        let realm = try await Realm(actor: RealmActor.shared)
        
        guard let object = realm.object(ofType: Self.self, forPrimaryKey: self.id) else {
            throw Error.someError
        }
        
        let thawedObject = object.safeThaw()
        
        // Execute within a write transaction
        return try await realm.asyncWrite {
            try block(thawedObject, realm)
        }
    }
    
    // Object retrieval
    @RealmActor static func get(id: ID, forceRefresh: Bool = false) async -> Self? {
        guard !Self.ignoredIds.contains(id) else { return nil }
        let realm = try! await Realm(actor: RealmActor.shared)
        if forceRefresh { await realm.asyncRefresh() }
        return realm.object(ofType: Self.self, forPrimaryKey: id) 
    }

    // Thawing helper function
    func safeThaw() -> Self {
        return realm == nil ? self : (self.thaw() ?? self)
    }
}

These async methods aren't normally usable within views (as far as I've tried, although feel free to correct me), so I still use the 'old' versions of these for retrieving objects on the main actor. Which are basically just synchronous versions of the above (using try! Realm() and realm.write, etc).

@tgoyne
No awaits occur within write transactions as far as I know. I always use the closure-based write functions, and since the closure parameters are not asynchronous themselves there's no way to do that, even accidentally.

If you want I can give some more code, or other assertion errors if I come across them.

@tgoyne
Copy link
Member

tgoyne commented May 24, 2023

#6661 definitely fixes this assertion failure, but if you're never using realm.beginWrite() then something else odd is going on too.

This is probably unrelated, but thaw() currently doesn't really work in async functions. There's intentionally no way for us to get the current actor so it currently returns an object confined to the current thread, which means that it's only guaranteed to be usable until the next await.

@Cheezzhead
Copy link
Author

Good to know, am I correct in assuming it won't do any harm at the moment either? I haven't run across frozen object problems in a while but kept this in as a safeguard.

I'm currently running into some other errors (EXC_BAD_ACCESS) after trying to clean up the code/adding debug logging. So not sure at the moment if the assertion failures are fixed because of the cleanup or because this new error is happening before the assertion can trigger. I'll make a new issue for the aforementioned error, and can resume testing this after it's fixed.

@sync-by-unito sync-by-unito bot closed this as completed May 29, 2023
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Mar 21, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

3 participants