IceCream icon indicating copy to clipboard operation
IceCream copied to clipboard

Versioning records

Open kitlangton opened this issue 5 years ago • 7 comments

This one is more of a brainstorm. Here's the problem:

The Problem

  1. A user has my flashcard app (for example) installed on an iPad and iPhone.
  2. I release an update, allowing users to add images to cards.
  3. They update their iPhone app and add some images.
  4. They continue to use their iPad app, without updating.
  5. The iPad version successfully synchronizes the updated cards, but of course, does not pull the images out of the record, because it does not yet know about them.
  6. The iPad user makes updates to these cards, which synchronizes them back up to the server, effectively deleting the images.

One Possible Solution

I'm not sure the best way to fix this. It is clearly one of the more complicated problems, with regards to multidevice asynchronous syncing. There's a blog post here https://www.toptal.com/ios/sync-data-across-devices-with-cloudkit, which suggests adding a version to each record.

If our local app encounters a higher version than it knows how to deal with—suggesting that the user has updated the app elsewhere—then we can abort all synchronization until the user updates their version of the app.

@dbmrq @caiyue1993 Have either of you under this problem before? Any thoughts? :)

kitlangton avatar May 15 '19 18:05 kitlangton

Nice catch! The versioning idea sounds good to me.

dbmrq avatar May 15 '19 19:05 dbmrq

I have seen this versioning method before - but for the life of me I can not remember where.

Anyhow. I think adding a version number is extremely sensible and will be a common issue - it's not an edge case. Thinking about myself I know we have 4 iPads in the house and I bet none of them share the same iOS version number.

Thinking about the implementation...... how would you generate or configure the version number? Could that be the Realm migration number?

ianbradbury-bizzibody avatar May 15 '19 20:05 ianbradbury-bizzibody

@ianbradbury-bizzibody I'm thinking something along the lines of:

  1. A static let version : Int on each Syncable class. The developer should increment this whenever the record structure changes in a way that would break things given the situation described above.
  2. Sync Engine Changes
   changesOp.recordChangedBlock = { [weak self] record in
            /// The Cloud will return the modified record since the last zoneChangesToken, we need to do local cache here.
            /// Handle the record:
            guard let self = self else { return }
            guard let syncObject = self.syncObjects.first(where: { $0.recordType == record.recordType }) else { return }
            
            /// NEW CODE
            if let version = record["version"], version > syncObject.version {
                // OMG! ABORT! CUT POWER TO THE SYNC ENGINE! WE'RE SINKING!
            }
            /// END
            
            syncObject.add(record: record)
        }
  1. Then, don't sync until the user updates the app?

kitlangton avatar May 15 '19 20:05 kitlangton

@caiyue1993, by the way, is there a test suite somewhere? If not, I might try to create one. It would be great to be able to test weird edge cases like this.

kitlangton avatar May 15 '19 20:05 kitlangton

Good idea!

GWesley avatar Jul 22 '19 18:07 GWesley

has anyone started working on this already? 😀

Do we have a timeframe?

johnnyperdomo avatar Jul 25 '19 22:07 johnnyperdomo

Any luck getting a fix / workaround for this issue? This is actually a big problem I'm facing as well.

kleber-maia avatar Aug 26 '20 14:08 kleber-maia