7

Do the official docs talk somewhere about CloudKit consistency? According to my tests it appears to be eventually consistent – reading a record immediately after writing it might work and might not (returning empty results):

CKDatabase *database = [[CKContainer defaultContainer] publicCloudDatabase];
CKRecord *record = [[CKRecord alloc] initWithRecordType:@"Foo"];

dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
[database saveRecord:record completionHandler:^(CKRecord *record, NSError *error) {
    CKQuery *query = [[CKQuery alloc] initWithRecordType:@"Foo" predicate:[NSPredicate predicateWithFormat:@"TRUEPREDICATE"]];
    [database performQuery:query inZoneWithID:nil completionHandler:^(NSArray *results, NSError *error) {
        XCTAssertEqualObjects(results, @[], @"Freshly written object not returned by query."); // succeeds
        dispatch_semaphore_signal(semaphore);
    }];
}];

dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);

Is there a way to force a strongly consistent read that would reflect all previous updates?

zoul
  • 102,279
  • 44
  • 260
  • 354
  • I am noticing the same thing in my tests. I am saving a record and waiting for that to successfully complete. If I then perform a query for all records, my new record is not returned. Have you found any information stating that CloudKit is eventually consistent? – Tim Dean Dec 19 '14 at 18:58
  • My use case was fortunately very simple, so I “solved” the issue with a kind of cache in the datastore layer: when an item is inserted to the datastore, it is written to iCloud _and_ stored on the client for a short while. And when the datastore layer is queried, it adds the living cache records to the result set. Stupid, but works. – zoul Dec 22 '14 at 10:04
  • 1
    @zoul ever find a better way to handle this? The last part of the inline comment in CKModifyRecordsOperation.h for modifyRecordsCompletionBlock suggests that this is the designed behavior: "**This call happens as soon as the server has seen all record changes, and may be invoked while the server is processing the side effects of those changes.**" – George Jan 24 '15 at 22:46
  • The server updates can take time and are not guaranteed to reflect the current records in the form of a query. If you however retrieve using the recordID it will be consistent. I think a delay is a dirty way to handle this. In the app that I have built that needs to keep track of real time records(at least from the users perspective) I have been saving changed RecordIDs, and moddate and checking the modification date against the queried record. If the record is not the freshest,I remove those from results and I retrieve those specific records by CKRecordID and stitch them into the results. – agibson007 May 28 '16 at 14:00

2 Answers2

9

It's a little of both: CloudKit is strongly consistent if you fetch a record by identifier, but eventually consistent when you fetch a record with a query.

When a CKModifyRecordsOperation returns successfully the record is immediately fetchable by its identifier.

However, it takes some time for the server to scan the record's values and update and distribute its search indexes. Until that indexing completes you won't see the record in any queries.

farktronix
  • 3,880
  • 1
  • 21
  • 27
0

My only experience with the term "eventual consistency" is from using CouchDB, which is eventually consistent. CloudKit is very different from CouchDB in that CouchDB allows for replicating distributed databases, whereas CloudKit only provides "services for managing the transfer of data to and from iCloud servers" - it is only a transport mechanism.

I'm pretty sure the transport mechanism itself is not eventually consistent - it saves and retrieves directly from the CloudKit server.

Since CloudKit is only a transport mechanism, you are responsible for maintaining your own local cache/database. Your local database would be considered eventually consistent, because it is not necessarily consistent in between syncs, and only becomes consistent when you eventually sync it using CloudKit.

Now, you say that in your tests you are saving and then attempting to retrieve a record. But I notice in your code that you are not checking for errors in the save operation. So, perhaps there is an error during the save that you are missing?

Richard Venable
  • 8,310
  • 3
  • 49
  • 52
  • I don't know about Apple's iCloud infrastructure, but I suspect there are many different iCloud servers running behind some kind of load balancing scheme. So it seems entirely possible that a call to save a record could be routed to one server instance while a subsequent call is routed to a different instance. Depending on how well iCloud keeps those instances in sync, having an eventual consistency model might be quite reasonable to expect here. – Tim Dean Dec 19 '14 at 19:01