We have one Persistent store coordinator(PSC). On top of this we have two managed object contexts(MOC), one with private queue concurrency(bgMOC)
persistentContainer.newBackgroundContext()
and another with main queue concurrency(mainMOC).
persistentContainer.viewContext
We have verified that only one PSC is connected to the sqlite. We only have one PSC object and only one bgMOC.
We always use bgMOC for writes.
And main for UI just reads and UI purposes.
We strictly follow access of managed objects in the contexts
.perform { }
always.
Problem: We see lots of Merge Conflicts
We have printed the conflict attributes and found
- object snapshot is always null
- cached snapshot is printed
- persisted snapshot is printed
Our findings:
- Sometimes cached snapshot is same as persisted snapshot and sometimes there is a diff in some property(not sure relationships diff as snapshot dont have these info).
- The NSManagedObject current values match the persistent store snapshot always. In one of the logs we concluded that the cachedRow was stale, and both the managed object and persisted values were same with newer values.
How do we proceed to fix this? Is cached row supposed to be synchronously updated with every save that happens? We dont want to put a merge policy as we think conflicts should not happen at first place as we are always writing via same bgMoc
Additional information
Our app does lots of quick(within 1-2 ms)
bgMoc.perform { change, bgMoc.save }
back to back and on same objets as well. Some places we have noticed if we try to dump a same change two times in quick succession via same bgMoc this crash happens. But we were not able to reproduce this on dev systems.
something like
bgMoc.perform { obj1.changeX, bgMoc.save }
bgMoc.perform {obj1. changeX, bgMoc.save }
We also do bgMocPerorm
inside bgMoc.perofrm
like this
bgMoc.perform {
on some managed objects validate a condition early exist if fails.
onSomeOtherThreadComputeSomeStuff { success in
bgMoc.perform {
someChange
bMoc.save()
}
}
}
Crash log
[Debug] [NSManagedObjectContext 0x303b11e10] [NSManagedObjectContext+Enhancements.swift:32] > Save error. Error : Error Domain=NSCocoaErrorDomain Code=133020 "Could not merge changes." UserInfo={conflictList=(
"NSMergeConflict (0x301aa97c0) for NSManagedObject (0x302045c70) with objectID '0x8d881a3c1b3524ba <x-coredata://412EE632-D802-451E-99DF-50ADF230B800/MailThread/p1416>' with oldVersion = 2 and newVersion = 3 and old cached row = {\n attachmentCount = 0;\n firstScheduledDate = \"<null>\";\n id = 2164154918090767;\n lastMessageDraftDate = \"<null>\";\n lastMessageReceivedDate = \"2024-07-16 18:31:34 +0000\";\n lastMessageSentDate = \"<null>\";\n messageCount = 1;\n rawFlags = 0;\n snippet = \"snippet1\";\n subject = \"subject1\";\n transactionIdAsNumber = 2164277798615057;\n umc = 1;\n} and new database row = {\n attachmentCount = 0;\n firstScheduledDate = \"<null>\";\n id = 2164154918090767;\n lastMessageDraftDate = \"<null>\";\n lastMessageReceivedDate = \"2024-07-16 18:31:34 +0000\";\n lastMessageSentDate = \"<null>\";\n messageCount = 1;\n rawFlags = 0;\n snippet = \"snippet1\";\n subject = \"subject1\";\n transactionIdAsNumber = 2164277798615057;\n umc = 1;\n}"