weixin_39532754
weixin_39532754
2020-11-30 10:17

Handling the remote synchronization of a deleted Outcome

Hi -apple, I've been looking at the tombstoning of an outcome when a "delete" occurs and it doesn't seem like the tombstoned outcome in updateOutcomesLeavingTombstone:

swift
currentOutcomes.forEach {
            $0.deletedDate = Date()
            $0.values = Set()
}

gets added to the revision record. I do see the new outcome in the revision record with a deletedDate set, but this leaves the original outcome with no deletedDate on the remote still referencing it's outcomeValue. So on the remote, the original outcome is still active, though in the OCKStore it's correctly tombstoned.

I'm guessing the logicalClock of the tombstoned outcome isn't being incremented when it's tombstoned, so changedQuery isn't picking it up, but I couldn't find where logicalClock should be updated

该提问来源于开源项目:carekit-apple/CareKit

  • 点赞
  • 写回答
  • 关注问题
  • 收藏
  • 复制链接分享
  • 邀请回答

5条回答

  • weixin_39532754 weixin_39532754 5月前

    I've tested this in OCKSample which uses a Parse Server as a remote by setting the currentOutcomes significantly high and it seems like my previous comment may be a step in the right direction (though I still can't find when the logicalClock gets changed during an update). I made the modification to the CareKit master here:

    swift
    currentOutcomes.forEach {
                $0.deletedDate = Date()
                $0.values = Set()
                $0.logicalClock = 100
            }
    

    I tapped "Kegel Exercises" to create an outcome, and then tapped it again to delete it. This puts the clock of the KnowledgeVector ~5 on the first run, so this tombstone was added to the revision record and pushed to the remote.

    If you setup the server in the readme, you can see the tombstone value being added to the Outcome table in the dashboard. If you use the master branch (uses the current CareKit master), you can see that tombstoned outcome is never added. I was checking to see if the tombstone was being added to the revision record here https://github.com/carekit-apple/CareKit/blob/f84d1ed481ed8a4f00abec745b7eec7f5583f86c/CareKitStore/CareKitStore/CoreData/Synchronization/OCKStore%2BSynchronization.swift#L304

    点赞 评论 复制链接分享
  • weixin_39532754 weixin_39532754 5月前

    I finally found where the logicalClock is initially set, https://github.com/carekit-apple/CareKit/blob/f84d1ed481ed8a4f00abec745b7eec7f5583f86c/CareKitStore/CareKitStore/CoreData/OCKCDObject.swift#L50

    It seems the awakeFromInsert method is only called by CoreData when the entity is first created which is why it never triggered during updates/deletes, but triggered during adds.

    点赞 评论 复制链接分享
  • weixin_39581972 weixin_39581972 5月前

    Thanks for working on this Corey! We'll try to look into this in more detail next week, but what you've written so far all sounds right to me.

    Since all the entities are intended to be immutable, it should be sufficient to only set the logical clock on insert, and then never update it again. But you're right that tombstoned outcomes can presently be missed unless their clock is manually bumped.

    点赞 评论 复制链接分享
  • weixin_39581972 weixin_39581972 5月前

    I'm going over your PR again and wanted to loop back on this issue as well!

    The current intended behavior is this:

    
    1. An outcome is created and sync'd to the remote
       - Device: 1 outcome
       - Server: 1 outcome
    
    2. The outcome is deleted locally
       - Device: 2 outcomes [1 tombstone, 1 "live" outcome with its deletedDate set]
    
    3. A diff is generated containing 1 record
       - The new "live" version with non-nil deletedDate
    
    4. The server receives the diff and inserts a new record. 
       - The presence of a new record indicates to the server it should tombstone its old record
    
    5. The server and the device both have two records.
       - Device: 2 outcomes [1 tombstone, 1 live outcome with its deletedDate set]
       - Server: 2 outcomes [1 tombstone, 1 live outcome with its deletedDate set]
    

    I believe your desired behavior is this (please correct me if I'm misunderstanding):

    
    1. An outcome is created and sync'd to the remote
       - Device: 1 outcome
       - Server: 1 outcome
    
    2. The outcome is deleted locally
       - Device: 2 outcomes [1 tombstone, 1 "live" outcome with its deletedDate set]
    
    3. A diff is generated containing 2 records
       - The tombstoned outcome. 
       - The new "live" version with non-nil deletedDate
    
    4. The server receives the diff and updates both of its records
       - Device: 2 outcomes [1 tombstone, 1 live outcome with its deletedDate set]
       - Server: 2 outcomes [1 tombstone, 1 live outcome with its deletedDate set]
    

    Your proposal makes implementing the server correctly just a bit less error prone by moving the responsibility for declaring the old outcome a tombstone onto CareKit instead of the integration developer.

    Does that accurately summarize this issue?

    点赞 评论 复制链接分享
  • weixin_39532754 weixin_39532754 5月前

    Yes! I will mention in step 4 in the “intended behavior” case, it currently seems hard to tell what outcome was tombstoned because the UUID of the outcome with the deletedDate set is different (new) from the original outcome. If the uuids were the same then it would be easy to tell what outcome to tombstone on the remote.

    Since a new uuid is generated, the “desired behavior” helps the remote know what to tombstone.

    点赞 评论 复制链接分享

相关推荐