Conflict resolution in CouchbaseLite

79 views
Skip to first unread message

Brendan Duddridge

unread,
Mar 17, 2016, 9:48:26 PM3/17/16
to Couchbase Mobile
Hi,

I'm having a couple of conflicts in my documents and I thought I was handling it properly, but it seems like I'm not able to get rid of the conflicted documents. I've implemented the recommended code as follows. I'm wondering if I've done something wrong?

- (BOOL)mergeConflictedDocument:(CBLDocument *)doc {


        NSError* error;


        NSArray* conflicts = [doc getConflictingRevisions: &error];


        if (conflicts.count > 1) {


                // There is more than one current revision, thus a conflict!


                [self.couchDatabase inTransaction: ^BOOL{


                        // Come up with a merged/resolved document in some way that's

                       // appropriate for the app. You could even just pick the body of

                       // one of the revisions.


                        NSDictionary* mergedProps = [self mergeRevisions:conflicts];


                        // Delete the conflicting revisions to get rid of the conflict:


                        CBLSavedRevision* current = doc.currentRevision;


                        for (CBLSavedRevision* rev in conflicts) {


                                CBLUnsavedRevision *newRev = [rev createRevision];


                                if (rev == current) {


                                        // add the merged revision


                                        newRev.properties = [NSMutableDictionary dictionaryWithDictionary: mergedProps];


                                        for (CBLAttachment *attachment in rev.attachments) {


                                                [newRev setAttachmentNamed:attachment.name

                                                          withContentType:attachment.contentType

                                                                  content:attachment.content];


                                        }


                                } else {


                                        // mark other conflicts as deleted


                                        newRev.isDeletion = YES;


                                }


                                // saveAllowingConflict allows 'rev' to be updated even if it


                                // is not the document's current revision.


                                NSError *error = nil;


                                if (![newRev saveAllowingConflict: &error]) {


                                        TFFLog(@"Error saving conflicted document: %@", error);


                                        return NO;


                                }


                        }


                        return YES;


                }];


        }

       return YES;


}



What's happening is even though the newRev.isDeletion is getting set and saveAllowingConflict is saving without an error, the conflict just doesn't seem to go away.

Just wondering if there's something else I should be implementing here to solve this problem?

Thanks!

Brendan

Brendan Duddridge

unread,
Apr 14, 2016, 4:19:27 PM4/14/16
to Couchbase Mobile
Further to this problem, I've just encountered it again.

When I look at the conflicts array from calling [doc getConflictingRevisions:], this is what I see:

CBLSavedRevision[rec-..fb1b/1-4098dccaa969cdbab64825e687aaec68],

CBLSavedRevision[rec-..fb1b/4206-5128082a4eb2783ba5e60116f84f7e07]




But each time through the above loop, a new revision is being added, so 4206 becomes 4207, ... n. and so on.

Is there anything wrong in the above code which would prevent the conflicted document from going away after the new revision apparently resolved the conflict?

Basically my replication has been going on for a very long time but can never get past this conflicted document.

Any suggestions are welcome.


Thanks,

Brendan

Jens Alfke

unread,
Apr 14, 2016, 4:43:02 PM4/14/16
to mobile-c...@googlegroups.com

On Apr 14, 2016, at 1:19 PM, Brendan Duddridge <bren...@gmail.com> wrote:

CBLSavedRevision[rec-..fb1b/1-4098dccaa969cdbab64825e687aaec68],

CBLSavedRevision[rec-..fb1b/4206-5128082a4eb2783ba5e60116f84f7e07]


But each time through the above loop, a new revision is being added, so 4206 becomes 4207, ... n. and so on.

So the 1-409… revision isn’t being deleted? The code in your previous message should end up deleting it. Have you tried stepping through it to see what’s going on?

—Jens

Brendan Duddridge

unread,
Apr 14, 2016, 4:54:06 PM4/14/16
to Couchbase Mobile
Yes, I've stepped through it. 

So what's happening is, the current revision is the one with the big number, e.g. 4206. The conflicted revision is the 1-409 revision. A new revision is created and then since it's not the current revision, the 1-409 revision is getting its "isDeletion" flag set to YES. And then I call [newRev saveAllowingConflict:] on the new revision. That succeeds without error. But then the next time the pull replication goes idle, offline, or stopped, I process all the conflicting documents again and we're back at square one.

Thanks,

Brendan

Brendan Duddridge

unread,
Apr 14, 2016, 4:55:47 PM4/14/16
to Couchbase Mobile
Sorry, I meant to say "the new revision is getting its "isDeletion" flag set to YES".

Maybe that's the problem? But I can't set the isDeletion flag unless I create a new revision it seems. So that 1-409 is never going away and I just keep creating new revisions over and over.

Jens Alfke

unread,
Apr 14, 2016, 7:36:57 PM4/14/16
to mobile-c...@googlegroups.com

On Apr 14, 2016, at 1:55 PM, Brendan Duddridge <bren...@gmail.com> wrote:

Sorry, I meant to say "the new revision is getting its "isDeletion" flag set to YES".
Maybe that's the problem? But I can't set the isDeletion flag unless I create a new revision it seems. So that 1-409 is never going away and I just keep creating new revisions over and over.

1-409 doesn’t go away but it acquires a child, 2-xxx, which is marked as a deletion. That means 1-409 is no longer a leaf, and 2-xxx is a leaf but it’s deleted so it doesn’t count as part of a conflict. The result should be no conflict.

It sounds like the update of 1-409 isn’t taking effect somehow … weird.

If you turn on logging for ‘CBLDatabase’ (or just ‘Database’, on the master branch) you'll get logs for newly created revisions. That might help figure out what’s going on.

—Jens

Brendan Duddridge

unread,
Apr 14, 2016, 7:38:34 PM4/14/16
to Couchbase Mobile
So just doing some more stepping through the debugger to try and figure out what's going on.

Before my loop, the array of conflicts from the document is as follows now:

(lldb) po conflicts

<__NSArrayI 0x610000020720>(

CBLSavedRevision[rec-..fb1b/1-4098dccaa969cdbab64825e687aaec68],

CBLSavedRevision[rec-..fb1b/4256-205cb128a6a5dfd1928ee4a6d38aa1f9]

)


then after I mark the revision as being deleted and I print out the list of conflicts again on my doc:

(lldb) po [doc getConflictingRevisions:nil]

<__NSArrayI 0x608000024160>(

CBLSavedRevision[rec-..fb1b/4256-205cb128a6a5dfd1928ee4a6d38aa1f9]

)


So now there's just one in the list as it looks like the 1- revision is gone. 

However, when I look at the CBLSavedRevision for the deletion, it has a revisionID prefix of 2:

CBLSavedRevision[rec-..fb1b/2-a106816d32bc283bc24acf98abeeec2d]


Now when the loop gets to the revision that's to be merged and then saves it, it now has the revision:

CBLSavedRevision[rec-..fb1b/4257-f4359eb63dc53f74a997adc1f606cd73]


So it's one higher now.

And now the document's conflicting revisions has this new revision in the array:

(lldb) po [doc getConflictingRevisions:nil]

<__NSArrayI 0x6080000255a0>(

CBLSavedRevision[rec-..fb1b/4257-f4359eb63dc53f74a997adc1f606cd73]

)



But then I'm back to where I started after the replicator pulls again:

<__NSArrayI 0x610000020860>(

CBLSavedRevision[rec-..fb1b/1-4098dccaa969cdbab64825e687aaec68],

CBLSavedRevision[rec-..fb1b/4257-f4359eb63dc53f74a997adc1f606cd73]

)


Again with the same conflict, but one revision higher now.

I'm not quite sure how to resolve this issue.

The _deleted flag in the revisions properties is definitely getting set right after the revision's isDeletion flag is set to YES.

(lldb) po [savedRevison properties]

{

    "_deleted" = 1;

    "_id" = "rec-a0ada90ce645458ebdb3ff7259c6fb1b";

    "_rev" = "2-a106816d32bc283bc24acf98abeeec2d";

    dateCreated = "2016-04-12T05:07:38.866Z";

    dateModified = "2016-04-12T05:07:38.866Z";

    dbID = "db-1deccd0e15dc4047ab96eba344144f3b";

    form = "frm-19f6c15c8f7f45c5a586d99685549697";

    type = "frm-19f6c15c8f7f45c5a586d99685549697";

    values =     {

        "fld-08c6bd02523b41899614960ddd45bee6" = Fawcett;


         .... lots of other values here I removed for this post ....


    };

}



And then the merged revision looks like this:

(lldb) po [savedRevison properties]

{

    "_id" = "rec-a0ada90ce645458ebdb3ff7259c6fb1b";

    "_rev" = "4257-f4359eb63dc53f74a997adc1f606cd73";

    dateCreated = "2016-04-12T05:07:38.866Z";

    dateModified = "2016-04-14T18:56:25.522Z";

    dbID = "db-1deccd0e15dc4047ab96eba344144f3b";

    form = "frm-19f6c15c8f7f45c5a586d99685549697";

    type = "frm-19f6c15c8f7f45c5a586d99685549697";

    values =     {

    };

}


The one with the empty "values" property is the most recent edit as I had deleted the values from the document, so 'values' should be empty. But the conflicted revision still has all the values.

Hopefully this more information can help shed some light on the situation.

Thanks,

Brendan

On Thursday, April 14, 2016 at 2:43:02 PM UTC-6, Jens Alfke wrote:

Brendan Duddridge

unread,
Apr 14, 2016, 7:50:22 PM4/14/16
to Couchbase Mobile
When I turn on Database logging this is what I get during conflict resolution:

Database: Begin transaction (level 1)...

Tap Forms[29851:8985729] is deletion: 1

Database: PUT _id=rec-a0ada90ce645458ebdb3ff7259c6fb1b, _rev=1-4098dccaa969cdbab64825e687aaec68, _deleted=1, allowConflict=1

Database: Duplicate rev insertion: rec-a0ada90ce645458ebdb3ff7259c6fb1b / 2-a106816d32bc283bc24acf98abeeec2d

Database: --> created {rec-a0ada90ce645458ebdb3ff7259c6fb1b #2-a106816d32bc283bc24acf98abeeec2d DEL}

Tap Forms[29851:8985729] Saved new revision: 2-a106816d32bc283bc24acf98abeeec2d, parent: (null)

Database: PUT _id=rec-a0ada90ce645458ebdb3ff7259c6fb1b, _rev=4272-a388b5ae2e82576f3bc56bfb1420c94e, _deleted=0, allowConflict=1

Database: Added: {rec-a0ada90ce645458ebdb3ff7259c6fb1b #4273-fce7a57ef00a2a6a243c91b44cb1ade7}

Database: --> created {rec-a0ada90ce645458ebdb3ff7259c6fb1b #4273-fce7a57ef00a2a6a243c91b44cb1ade7}

Tap Forms[29851:8985729] Saved new revision: 4273-fce7a57ef00a2a6a243c91b44cb1ade7, parent: 4272-a388b5ae2e82576f3bc56bfb1420c94e

Database: Commit transaction (level 1)

Database: CBLDatabase[<0x608000121900>db-1deccd0e15dc4047ab96eba344144f3b]: Posting change notifications: seq 123627

Database: CBLDatabase[<0x6100001221c0>db-1deccd0e15dc4047ab96eba344144f3b]: Notified of 1 changes by CBLDatabase[<0x608000121900>db-1deccd0e15dc4047ab96eba344144f3b]

Database: CBLDatabase[<0x6180001258c0>db-1deccd0e15dc4047ab96eba344144f3b]: Notified of 1 changes by CBLDatabase[<0x608000121900>db-1deccd0e15dc4047ab96eba344144f3b]

Database: CBLDatabase[<0x6100001221c0>db-1deccd0e15dc4047ab96eba344144f3b]: Posting change notifications: seq (123627)

Database: CBLDatabase[<0x6180001258c0>db-1deccd0e15dc4047ab96eba344144f3b]: Posting change notifications: seq (123627)



Some of the messages are my own logging and I took out the timestamps just so it wasn't so wide for this posting.

It just gets stuck in this loop spitting out the above messages over and over.

Thanks,

Brendan

Brendan Duddridge

unread,
Apr 15, 2016, 3:34:44 PM4/15/16
to Couchbase Mobile
So do you think maybe deleting the entire CBLDocument and re-creating it might be a workaround for this problem? I still don't know why my 1- revision isn't getting deleted when I tell it to. Could it be that the new revision has such a large number? I mean, that it's not just one digit above the conflicted revision? I did let this process run for quite a while and that's why it's up over 4000- for the prefix of the revision ID.


Thanks,

Brendan

Jens Alfke

unread,
Apr 15, 2016, 5:04:24 PM4/15/16
to mobile-c...@googlegroups.com

On Apr 14, 2016, at 4:50 PM, Brendan Duddridge <bren...@gmail.com> wrote:

Database: PUT _id=rec-a0ada90ce645458ebdb3ff7259c6fb1b, _rev=1-4098dccaa969cdbab64825e687aaec68, _deleted=1, allowConflict=1

Database: Duplicate rev insertion: rec-a0ada90ce645458ebdb3ff7259c6fb1b / 2-a106816d32bc283bc24acf98abeeec2d

This is strange: it’s saying that the revision deleting rev 1-409 already exists, so the operation is a no-op.
But if that’s true, then rev 1-409 isn’t a leaf, so it can’t possibly count as a conflict.

I’m thinking now that there’s something wrong in the structure of the database — most likely that the `current` column of the row for rev 1-409 is mistakenly set to `1` when it should be `0`.

Would it be possible for you to email me a copy of the database (the .cblite2 bundle)?

—Jens

Brendan Duddridge

unread,
Apr 15, 2016, 5:58:42 PM4/15/16
to Couchbase Mobile
Yes, absolutely. I'll send it on.

Brendan Duddridge

unread,
Apr 15, 2016, 6:20:17 PM4/15/16
to Couchbase Mobile
Damn. Ok, maybe I won't be sending it on. I had turned off the processing of conflicts for a while and was working with the database as is and now for some reason even after I re-enabled conflict processing, there seems to be no more conflicts in the database. And wouldn't you know it, my Time Machine NAS drive isn't responding right now so I don't have a backup anymore. Well, I guess all I can do is keep an eye on it and if it ever happens again I'll send it on. 

Sorry about that.

Brendan
Reply all
Reply to author
Forward
0 new messages