-
Notifications
You must be signed in to change notification settings - Fork 2.2k
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
Getting reference to realm object that may or may not exist #8426
Comments
The following implementation using write works, but I would like to do this using asyncWrite. func fetchOrCreateUser(forPrimaryKey: String) async throws -> User? {
let user = try await MainActor.run {
let realm = try Realm()
let user = try realm.write {
let user = realm.object(ofType: User.self, forPrimaryKey: forPrimaryKey)
if let user = user {
return user
} else {
let newObject = User(value: ["userId": forPrimaryKey])
realm.add(newObject, update: .modified)
return realm.object(ofType: User.self, forPrimaryKey: forPrimaryKey)!
}
}
return user
}
return user.freeze()
} and func write(object: User) async throws {
try await MainActor.run {
let realm = try Realm()
try realm.write {
realm.create(User.self, value: object, update: .modified)
}
}
} and everywhere else accessing realm only using try await MainActor.run {
let realm = try Realm()
// do something
} |
There may be some confusion due to how Realm named these functions: The question states
It should be noted that asyncWrite and writeAsync are two different functions. The writeAsync() API allows for performing async writes using Swift completion handlers. The asyncWrite() API allows for performing async writes using Swift async/await syntax. The asyncWrite() API suspends the calling task while waiting for its turn to write rather than blocking the thread. In addition, the actual I/O to write data to disk is done by a background worker thread. Just for clarity. |
You are totally right, sorry for writing about |
One more thing I just tried: I made a global actor like so: @globalActor
actor RealmBackgroundActor {
static var shared = RealmBackgroundActor()
} And I marked the class in which I call functions (and the functions itself, just to be sure) with @RealmBackgroundActor func fetchOrCreateUser(forPrimaryKey: String) async throws -> User? {
let realm = try await Realm(actor: RealmBackgroundActor.shared)
let user = try await realm.asyncWrite {
let user = realm.object(ofType: User.self, forPrimaryKey: forPrimaryKey)
if let user = user {
return user
} else {
let newObject = User(value: ["userId": forPrimaryKey])
realm.add(newObject, update: .modified)
return newObject
}
}
return user.freeze()
} and @RealmBackgroundActor func write(object: LSNObject) async throws {
let realm = try await Realm(actor: RealmBackgroundActor.shared)
try await realm.asyncWrite {
realm.create(type(of: object), value: object, update: .modified)
}
} But still, the user objects gets overwritten (i.e. the userName lost) if two let senderUserId = "usr1"
Task {
let senderUserTest = try await fetchOrCreateUser(forPrimaryKey: senderUserId)
// user is needed for something
}
Task {
let newUser = User(userId: senderUserId, userName: "THISGETSLOST")
try await write(object: newUser)
} |
How frequently does the bug occur?
Always
Description
I have a RealmSwift object
User
, I want to simply load the object into memory by it's primary key. If no object with that primary key exists, I want to create one and get it back as well. I have a concurrent write that modifies the same object (same primary key).I have written the following function:
Independently User objects get populated with data by using:
However, in our application (where there is concurrency) users get independently populated. The code above results in
User
objects that are populated with data (using thewrite(...)
above) being overwritten, only leaving their "userId" property set. This seems like the realm doesn't get properly locked during the "asyncWrite" transaction.In my understanding, it does not matter in what order I call the two functions, since the documentation on
asyncWrite
states:So the write lock should still happen (everything else would also be odd, since race conditions would easily happen...)
And further the docs state:
So clearly, both of the asyncWrites in my case should run after each other, in whatever order. Both of these should be running on the same thread, namely the main thread...
This is an example of data being lost by this issue:
If I use
write
instead ofasyncWrite
I get the errorStacktrace & log output
Version
10.44
What Atlas Services are you using?
Local Database only
Are you using encryption?
No
Platform OS and version(s)
iOS 16, 17 and potentially other
Build environment
Xcode version: Version 15.0 (15A240d)
Dependency manager and version: SPM
The text was updated successfully, but these errors were encountered: