YapDatabase icon indicating copy to clipboard operation
YapDatabase copied to clipboard

recordChangeTag not getting saved after YapDatabaseCloudKitTransaction.mergeRecord

Open zsau opened this issue 8 years ago • 5 comments

I posted about this on the mailing list, but no replies so far. To summarize, it seems that when I call YapDatabaseCloudKitTransaction.mergeRecord with a record I received from a CKFetchRecordChangesOperation, the new record's recordChangeTag isn't getting saved.

It's likely this is just a bug in my code, but I can't see what I'm doing wrong if so. To compound the problem, I can't figure out how to set YapDB's logging level. YapDatabaseLogging.h has various #defines, but it's not clear to me how to actually use them, especially in the context of CocoaPods.

zsau avatar Aug 22 '16 09:08 zsau

I figured out how to enable logging, but it hasn't helped so far. I've looked over my code many times and compared it to CloudKitTodo, but can't see why my remote record's change tag isn't getting saved.

In case more detail would help: on startup, my app suspends the YDBCK extension and does a CKFetchRecordChangesOperation before resuming it. Taking the record I get from that operation, I compare its change tag to YDBCK's local record change tag (obtained via getRecordChangeTag) and see that they are different (let's say the local record's change tag is "a" and the remote record's tag is "b").

So I invoke mergeRecord, which calls my mergeBlock with what seem like the right values: pendingLocalRecord is nil, and updatedPendingLocalRecord has the new change tag "b" as I'd expect. All my mergeBlock does then is call setObject:forKey:inCollection:, and everything seems to work.

But the next time my app modifies the database object with that key/collection, YDBCK invokes my recordHandler with a record whose change tag still has the old value "a". So of course when YDBCK then tries to send the updated record to CloudKit, it gets a ServerRecordChanged error.

AFAICT I'm doing everything as I'm supposed to, so I'm stumped. Any help whatsoever would be much appreciated.

zsau avatar Aug 30 '16 15:08 zsau

Things are even stranger than I thought. The new recordChangeTag is getting saved, at least according to what getRecordChangeTag tells me after my CKFetchRecordChangesOperation (and the transaction it creates) finishes. So the current situation is:

  • YDBCK does save the new remote recordChangeTag somewhere after my CKFetchRecordChangesOperation.
  • Nevertheless, the next time that database record changes locally, YapDatabaseCloudKitTransaction._handleChangeWithRowid is somehow still seeing the old recordChangeTag, which causes conflicts when YDBCK tries to push the change to CloudKit.

At this point I can only suspect a bug in YDBCK, but I don't currently understand its internals well enough to get any farther. Can a developer please help me out here?

zsau avatar Sep 07 '16 03:09 zsau

With a clearer picture of what's happening, I'm now reasonably certain this is a YDBCK bug. To summarize:

My code asks YDBCK to merge a record received from CloudKit with an existing local record. My mergeBlock just calls transaction.setObject to save the remote record, and doesn't touch updatedPendingLocalRecord. But YDBCK doesn't save the new CKRecord to its internal cloudKit_record table, meaning its recordChangeTag is now stale, which causes conflicts the next time YDBCK tries to update that record.

@robbiehanson , if you don't currently have time to look at this, could I instead get a brief explication of the execution path by which YDBCK saves updated CKRecords to disk after a merge? If I understood that better, I'd have a higher chance of tracking down what's going wrong.

zsau avatar Oct 28 '16 14:10 zsau

This comment in YapDatabaseCloudKitTransaction.m made me suspicious, since there should be no CKModifyRecordsOperation involved when I'm refetching and merging a record.

So I tried changing the maybeUpdateRecordTableRowWithHash:info: call below it to updateRecordTableRowWithHash:record:outSanitizedRecord:. That does seem to solve the merging problem, but my understanding of YDBCK is not deep enough to say whether it's the right approach or might break something else.

@robbiehanson, any comments?

zsau avatar Nov 12 '16 02:11 zsau

@zsau Maybe it is better to check if the change is coming from remote merge. Here is my fix: https://github.com/ainopara/YapDatabase/commit/5e9bf96e94c6a1d4014b1b0f963ed6b4cef341b4

ainopara avatar Apr 24 '18 04:04 ainopara