When resolving conflicts is there a way to resolve to the server revision?

I am using couchbase lite 1.2.1 on iOS. Using pull replication I detect conflicts and want to resolve them such that the server revision is the one that wins. Is there a way to do this?

Delete the local revision (update it and set the isDeletion property on the new revision.)

How do I know which the local revision is?

When you observe database-changed notifications, the DatabaseChange object for a revision pulled from a server will have its source property set to a non-null value.

We don’t have a persistent flag on a revision that says whether it was locally-created. That might be a good idea for the future, though.

I hadn’t noticed CBLDatabaseChangeNotification before. So instead of checking for conflicts when the pull stops, I can monitor that notification and resolve conflicts there. Seems like that could work. Thanks for the fast response.

I hit an issue working with this notification. If I handle the notification on the mainQueue I get periodic lockups on startup, if I handle it on the notification thread I get errors about working with the database on the main thread.

__weak DatabaseManager *weakSelf = self;
 [[NSNotificationCenter defaultCenter] addObserverForName:kCBLDatabaseChangeNotification
                                                      object:nil
                                                       queue:nil //or [NSOperationQueue mainQueue]
                                                  usingBlock:^(NSNotification * _Nonnull note) {
                                                      NSArray<CBLDatabaseChange *> *changes = note.userInfo[@"changes"];
                                                      [weakSelf databaseDidChange:changes];
                                                  }];

The warning when queue is nil:

14:27:20.050‖ WARNING: Exception caught in -[CBLDatabase doAsync:]:
***** THREAD-SAFETY VIOLATION: This database is being used on a thread it wasn't  created on! Please see the concurrency guidelines in the Couchbase Lite documentation. *****

When using the mainQueue. Here are the two relevant threads that get deadlocked…

main thread:
frame #4: 0x000000018224cc50 CoreFoundationCFRunLoopRunSpecific + 384 frame #5: 0x0000000182c5ccfc Foundation-[NSRunLoop(NSRunLoop) runMode:beforeDate:] + 308
frame #6: 0x00000001009c1e94 ConceptsMYWaitFor + 264 frame #7: 0x000000010099d34c Concepts-[CBLDatabase waitFor:] + 64
frame #8: 0x00000001009a1464 Concepts`-[CBLLiveQuery waitForRows] + 144

other thread:
frame #0: 0x0000000181f07f24 libsystem_kernel.dylib__psynch_cvwait + 8 frame #1: 0x0000000181fd2ce8 libsystem_pthread.dylib_pthread_cond_wait + 648
frame #2: 0x0000000182d268ec Foundation-[__NSOperationInternal _waitUntilFinished:] + 132 frame #3: 0x0000000182c5947c Foundation-[__NSObserver _doit:] + 232
frame #4: 0x0000000182310dfc CoreFoundation__CFNOTIFICATIONCENTER_IS_CALLING_OUT_TO_AN_OBSERVER__ + 20 frame #5: 0x000000018231061c CoreFoundation_CFXRegistrationPost + 396
frame #6: 0x000000018231039c CoreFoundation___CFXNotificationPost_block_invoke + 60 frame #7: 0x0000000182379414 CoreFoundation-[_CFXNotificationRegistrar find:object:observer:enumerator:] + 1532
frame #8: 0x000000018224e6c8 CoreFoundation_CFXNotificationPost + 368 frame #9: 0x00000001009d1a94 Concepts__42-[CBLDatabase(Internal) postNotification:]_block_invoke + 64
frame #10: 0x000000010099cfac Concepts`catchInBlock + 28

The end solution was to dispatch_async in the notification handler. e.g.

    _weak DatabaseManager *weakSelf = self;
    [[NSNotificationCenter defaultCenter] addObserverForName:kCBLDatabaseChangeNotification
                                                      object:nil
                                                       queue:nil
                                                  usingBlock:^(NSNotification * _Nonnull note) {
                                                      dispatch_async(dispatch_get_main_queue(), ^ {
                                                          NSArray<CBLDatabaseChange *> *changes = note.userInfo[@"changes"];
                                                          [weakSelf databaseDidChange:changes];
                                                      });
                                                  }];

Can you see any potential problems with doing this?

One thing that would be useful would be to report the database in the notification. I have to get this via a document lookup at the moment. (we have two CB databases in our app).

Also, as you say it would be really useful to track the source on the revision. Should I open an issue on github for that?

Don’t use object: nil when registering — you’ll get notifications for every CBLDatabase, including ones on background threads (like the internal instance used by the replicator.) Only register for the specific CBLDatabase(s) you want to monitor.

1 Like