Core Data Crash: Could not merge changes

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

  1. object snapshot is always null
  2. cached snapshot is printed
  3. persisted snapshot is printed

Our findings:

  1. 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).
  2. 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}"
Core Data Crash: Could not merge changes
 
 
Q