Delete Object While In write transactions

I am seeing some unexplained behavior when working with a Fully Sync’d Realm Object Server. Let me give a 10k foot overview first.

The use case is a multi-user ‘Tasks’ app where the UI is just a list of tasks and user names who is responsible for each task. The task details can be viewed and edited in another window (like Sheet or a detail view)

For this example when a task detail view opens, the first thing that happens is realm.beginWrite to put realm in a write transaction. Then when the details window closes with realm.commitWrite.

Overlooking that app design for a minute, that should put Realm inside a transaction the whole time the details view is open.

But here’s what we are seeing.

If User A opens a task detail view and then user B opens a task detail view, then user B closes it, their client crashes with

The Realm is already in a write transaction

That’s to be expected and the call throws, so it can be handled.

However, Client A closes their window and they also get a crash with the same error.

and here’s the concerning behavior. Sometimes when Client A starts a write transaction, Client B can add, edit delete objects at the same time.

If Realm is ACID compliant, we shouldn’t be seeing that behavior as that’s not really atomic: where a transaction is either succeeds completely, or fails completely. It’s also not Isolation as it seems the transactions are not executed sequentially; one client accessing a Realm within a transaction and the other is accessing (writing/deleting) the exact same data - even editing the same object

This is pretty easy to duplicate in code and I am probably overlooking a mechanic.

Ideas?

Perhaps I’m missing something, but this seems like a different bug in your app. If user A and user B are different devices, then they should not interact with each other on the transaction level. That is to say, any transactions opened on any device are local to that device only. To reiterate, if I’m using the app on my iPhone and my iPad, I can freely open and commit transactions on each device and the other would have no knowledge of that.

Once I commit transaction on the first device, that would trigger sync to pick up the changes and upload them to the server. After that those changes will be sent down to the second device, a write transaction will be opened and these changes will be integrated. Keep in mind that this write transaction will not result in the error you’re seeing because it happens on a different thread. What would happen though is that it will need to acquire a write lock, so it will wait for the other transaction to complete.

Thanks for the response and explanation.

they should not interact with each other on the transaction level.

Understood that the devices do not interact with each other at the transaction level.

I think the question comes down to what is the order of commit on the server - the docs are a bit unclear as to what happens if, for example, an transaction is started on a client, a different client makes changes and commits, and then the first client commits afterwards.

I put together a use case demo but our Realm apparently just crashed with

Operation Canceled

Even if we delete the Realm and all of the local files, it won’t let us back in. We had valid data but it appears to be gone. Obviously off topic.

The operation canceled issue seems like a bug and should probably file a support ticket so the team can help. To your question - I think I need to clarify that all transactions at Realm (even on synchronized Realms) are local for that device and its local database. Transaction opening and committing is in no way communicated to the server which is why synchronized Realms work offline. After the transaction is committed locally, the local sync process picks up the changes and sends them to the server. As to which changes make it to the server first - it’s not deterministic - e.g. a device can commit first, but its network may be very slow, or drop packets and its changes may make it to the server after a device that committed at a later point but was on a faster network. The good news though is that the server will automatically handle conflicts and do the right thing regardless of which order the changes came. E.g. if I change a property on device A and change the same property on device B. Regardless of when these devices upload their changes (e.g. one of them may have been offline and so on), the server will decide to keep the change that was made “last”.

We stepped away for several hours without touching code or server and upon returning were able to delete the Realm completely (small data loss) and recreate it. It’s now working. We have a ticket open.

Thank you for the detailed explanation - I would really like to see that clearly in the documentation somewhere as it’s a pretty important point.

On this…

the server will decide to keep the change that was made “last”.

It appears that statement is correct in the cases of object updates but not in the case of delete’s. If device A has a transaction open to modify an object property for example, and device B opens a transaction, deletes that object and commitsWrite, followed by device A commitWrite, that’s a throw/crash and the object is deleted even though device A committed ‘last’.

We can work with that now that we have a better understanding of the background processes

Thank you

There should be no crashes on the device or the server regardless of the order of transactions, so if you experience such and can reproduce them, it’d help the team greatly to fix them.

Regarding your experience with deletions - you are correct. The conflict resolution algorithm (the piece of code that triggers when two clients independently of one another work on the same object) will always favor a delete over update. E.g. if client A updated a Person record and changed their name property, but client B deleted that person altogether, then the deletion would “win” against the update and the final result (after all clients have synced to the server) will be that the object will be gone.

There are various rules for the different types of conflicts that may arise, such as adding/moving/deleting elements from a list or creating objects with the same primary key, but different other properties and so on. We don’t have these publicly documented, but if knowing them is important for your use case, you should reach out to the support team and see if they can arrange to share them with you.