Solve conflicts on client side (Android)

Is this code snippet correct? My conflict strategy is to choose the latest rev version. I receive the docs with the all-docs query and flag ONLY_CONFLICTS.

private void resolveConflicts(final Document doc, final List<SavedRevision> conflicts) {
    if (conflicts.size() > 1) {
        // There is more than one current revision, thus a conflict!
        CouchbaseUtil.getDatabase().runInTransaction(new TransactionalTask() {
            @Override
            public boolean run() {
                try {
                    SavedRevision current = doc.getCurrentRevision();
                    for (SavedRevision rev : conflicts) {
                        UnsavedRevision newRev = rev.createRevision();
                        if (!rev.getId().equals(current.getId()))
                            newRev.setIsDeletion(true);
                        // saveAllowingConflict allows 'rev' to be updated even if it
                        // is not the document's current revision.
                        newRev.save(true);
                    }
                } catch (Exception e) {
                    return false;
                }
                return true;
            }
        });
    }
}

This code is run every time the Replication Changelistener fires an event. Is this a good place to do so?

new Replication.ChangeListener() {
    @Override
    public void changed(Replication.ChangeEvent event) {
        // some other code and conflict solving
    } 

Hi @benjamin_glatzeder,

  • resolveConflicts() method looks good.
  • Replication.ChangeListener()'s changed() is fired whenever replication has updates. If you are using continuous replication mode, run conflict resolution when replication state becomes IDLE. If you are using one-shot replication mode, run conflict resolution when replication stopped.

Thank you for the tip to wait for IDLE state

if (!rev.getId().equals(current.getId()))

I’m not very familiar with the Java API, but I believe getID returns the document ID, not the revision ID. If so, use getRevID instead. (Is that right, @hideki?)

My conflict strategy is to choose the latest rev version.

That’s not what the code does, actually. The current revision is not necessarily the latest one; it’s just picked basically at random to break a tie. If you really want the latest revision to win, you’ll need to add a timestamp property to the document that gets updated right before it’s saved, and then compare those properties instead. (But be aware that timestamps are not reliable in a distributed system: some devices may have clocks that are accidentally or deliberately wrong.)

SavedRevision.getId() returns Revision ID.

Note:
getDocID() and getRevID() are an internal method of RevisionInternal class.

OK. Let me phrase my conflict strategy differently:

it needs to be:

  • deterministic. Given the same input the output should always be the same. Is this not the case at the moment? Is it always a random revision

This is what I know about rev IDs:

  • they start with an integer followed by a dash and a possibly unique string of characters
  • the integer always increases by 1
  • there is a conflict if there are two or more rev IDs with the same integer but different string of characters.

I think that:

  • my code runs when there is a conflict
  • the code always chooses the same winner if the input (all rev IDs) is the same. And all other rev IDs are marked to be deleted

I’m looking forward to your comment

Yes, the current revision is always deterministically chosen; that’s important because it ensures that all up-to-date-peers agree on what revision is current.

(The string of characters in the revID is a digest of the contents, so it’s effectively random. The current revision is the one in the highest generation [integer] with the highest digest as compared by strcmp.)

Thank you for the additional information!