-
Notifications
You must be signed in to change notification settings - Fork 2.1k
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
"Cannot register notification blocks from within write transactions." when making changes in quick succession #8333
Comments
I'm also seeing this in our app recently. |
same here. eek |
I tried
|
Can we get some eyes on this? It's a blocker bug for us. |
Affirm. We only use |
Hmm - we did merge a PR here - Are you using the latest? |
@ianpward I think so. I don't know how to get the exact version of "Realm" (core), but I see "10.42.0 Release notes (2023-07-30)" in the changelog. |
Have you followed the recommendations here - and here - |
@ianpward What should I observe?
I am trying to add a new item to a collection, the recommendation seems to apply only to existing items. Edit:
The item coming from |
@bogdan-pechounov
Second, you are getting the second error because you are not supposed to add the same item to the realm and item in this case is of type I'm a little bit confused about your sample.
|
@dianaafanador3 I was trying to replicate this code, but for adding a new item:
If I only change the item name, then there are no errors.
The error occurs when adding a new item. (e.g. pressing the plus button repeatedly, typing and then pressing the plus button)
|
I am using 10.42.0 of RealmSwift, but it doesn't appear to pull in a newer core artifact than 13.7.1? I'll try updating the |
@ianpward the let coreVersion = Version("13.17.1") |
@bogdan-pechounov I was able to reproduce your issue. The error mentioned on this issue can be a little bit misgiving, this is not happening because you are registering a notification in a write transaction, is because your write is happening while the realm is in a transaction, which we verify later during the write commit and throw an exception. @jeffypooo do you have the same use case as the one described above, do you get this error while on SwiftUI View while using |
@dianaafanador3 To clarify, does the binding Is the reason such a check exist is in case we create a new item that depends on the entire collection, e.g
(the check is only for Is there a way to queue transactions? |
There's currently no debouncing. We looked into adding it and concluded that it was going to be sufficiently complicated that we might as well put the effort into making small writes work better instead. Write transactions can't happen "at the same time" as they involve a global (per-Realm) lock. However, in that code you're checking for the max outside of a write transaction, and so may be reading a stale value and end up with a duplicate. Wrapping the whole thing in an explicit write transaction is required for correct results: realm.write {
let order = (items.max(of: \Item.order) ?? 0) + 1
let item = Item(name: name, order: order, userId: app.currentUser?.id)
$items.append(item)
} Async writes are the mechanism to queue writes. If you call |
@tgoyne Are actors necessary, or can I just use
(doesn't fix the error) Also, I noticed that the error doesn't occur if I remove the line
Since the closure is executed on the main thread, maybe animating the keyboard up interferes with realm in some say?
|
If I simply use
Edit: This only applies to pressing the plus button repeatedly. Typing while pressing the plus button will still produce an error. @jeffypooo @aehlke How does the error occur for you? (by adding repeatedly while using |
We usually encounter this when trying to start change observation as a result of a write, but it does seem to be limited to situations where the presentation mode is changing. The most common occurrence for us is when a button is clicked that:
|
Interesting that both reproduction cases mentioned involve buttons as well |
|
We are also encountering this issue. When asnyWrite (or normal writes, issue happens either way) are triggered in rapid succession (in our case caused by quick server requests) and there is a notification block observing this collection (no writes in the notification block). This happens even with everything realm on the @mainactor. |
I'm running into this on latest realm-swift, iPadOS 16.5 with debugger attached. No movement on this in 3+ months :/ I am checking immediately beforehand isInWriteTransaction as well... |
Is anyone seeing this more in development vs production? I am getting hit with it constantly in development due to a write transaction beginning but unclear what else is happening concurrently/elsewhere at the same time that could be conflicting with that... I'm worried about releasing to production in this state given how often I get it on macOS during development. @dianaafanador3 I get this with many views having both ObservedResults and ObservedRealmObject, per your earlier question... Is there something else we can provide? |
This no longer happens for us when performing all writes on a (single) background actor. We use a global actor for this like so: @globalActor
actor RealmBackgroundActor {
static var shared = RealmBackgroundActor()
} We also observe on this actor only, and use exclusively So we observe using: @RealmBackgroundActor
func observeCollections() async throws {
let realm = try await Realm(actor: RealmBackgroundActor.shared)
let results = realm.objects(SomeProtocol.self)
resultNotificationToken = results.observe { [weak self] (changes: RealmCollectionChange) in
self?.objects = Array(results.sorted(by: \.createdAt).freeze())
}
} and write using: @RealmBackgroundActor
func write(object: SomeProtocol) async throws {
let realm = try await Realm(actor: RealmBackgroundActor.shared)
try await realm.asyncWrite {
realm.create(type(of: object), value: object, update: .modified)
}
} Hope this helps someone:) |
Thanks for sharing. That would require a full rewrite of my RealmSwift usage, to remove all SwiftUI usage and so on... Difficult choice for a large project. I hope there is another solution to this constant crashing. edit: did you test having more than one realm bg actor? it's too bad to not be able to parallelize even different .realm files |
@bogdan-pechounov that commenter suggests using edit: I've begun implementing @davidkessler-ch 's solution and it's working for me as well. Too bad it means a full rewrite of all my realm usage to async actors, and maybe ditching ObservedResults. I hope others don't wander into this problem with huge lock-in on non-async actor approaches that are still advocated for in official docs. |
update: I've converted all Realm writes to asyncWrite on a new globalActor and inside Task.detached but still facing constant crashing (which has worsened recently). Now removing all usage of This makes me want to abandon Realm ASAP - without being able to use any of the SwiftUI conveniences and inaction for years on this issue, the upside is getting hard to appreciate |
@aehlke In our case I believe the core problem was exactly the parallelisation that lead to the unexpected behaviour, which is fair, considering that opening many realms in async context without specifying any actors. Now that being said, I also think this could be handled way better, e.g. by having the registration for the change listener be async as well, waiting for any write locks to finish before registering. Or waiting to finish any change listener registration before allowing to open any write transaction. What frustrates me the most, is that realm does not allow for us to catch errors, but rather crashes the app, since they say it's "considered to be programmer error". In many cases this makes sense, but with these very fine grained, sometimes hard-to-reproduce problems in concurrency, going into production is just a huge pain. |
It turns out that while 10.39.1 was supposed to make it so that notification blocks could be registered from within write transactions until changes were actually made, this didn't actually work. #8439 makes it so that it does. |
@davidkessler-ch appreciate the thoughts @tgoyne great news, thanks for the update For my code, I found a spot I'd missed with observation happening off the custom globalActor and fixed that; seems stable now that ALL writes AND observations happen on a custom globalActor and in Task.detached, without needing to finish ridding my code of ObservedRealmObject etc |
@tgoyne I updated the package (RealmDatabase 13.25.0, iOS 17.2) and there are no longer any issues. Thank you! |
How frequently does the bug occur?
Always
Description
I have a
List
ofTextField
. When I add items and edit their names quickly, I get a "Cannot register notification blocks from within write transactions" error.The error doesn't happen if I only update the item name while typing fast. If I press the plus button while typing fast, the error sometimes occurs.
Stacktrace & log output
Can you reproduce the bug?
Always
Reproduction Steps
Click on the "+" button on the top right repeatedly until the error occurs (on a "iPhone 14 Pro Max - iOS 16.4" emulator)
Optionally, type at the same time (in this case, the plus button doesn't need to be pressed as quickly)
Version
13.17.1
What Atlas Services are you using?
Local Database only
Are you using encryption?
No
Platform OS and version(s)
macOS 13.4.1
Build environment
Xcode version: 14.3.1
Dependency manager and version: ...
The text was updated successfully, but these errors were encountered: