All Documents Query for Conflicts Only is returning docs with no conflicts

I am debugging why we have so many conflicts reported on our customer databases, and while digging through a database I found a few documents that have no conflicts but are being reported as in conflict by the all docs query to retrieve conflicts only. We are using CouchbaseLite 1.4 on iOS. Here is our conflict detection code:

CBLQuery *query = [database createAllDocumentsQuery];
    [query setAllDocsMode:kCBLOnlyConflicts];
    [query runAsync:^(CBLQueryEnumerator *result, NSError *error) {
        
        // Merge document conflicts, if applicable.
        BOOL isMergedConflictSuccessfully = false;
        if (IS_NOT_NULL(result)) {
            for (CBLQueryRow *row in result) {
                if (IS_NOT_NULL(row) &&
                    IS_NOT_NULL([row conflictingRevisions])) {
                        isMergedConflictSuccessfully |= [appDelegate mergeRevisions:[row conflictingRevisions] intoDatabase:database];
                }
             }
        }
 }

Here is the _sync object for an example document that is reporting a conflict. It is one that the app never saves, it is only touched on the database, and the server reports no conflicting revisions on it:
"_sync": {
“rev”: “10-ac08f67084a93b63a720e3f1500304fa”,
“sequence”: 13685316,
“recent_sequences”: [
13462675,
13540770,
13560263,
13560289,
13560546,
13560596,
13560608,
13560609,
13651267,
13685316
],
“history”: {
“revs”: [
“8-e13f66f27c77afb10160be83ffd7e9d5”,
“4-de7c8257087e0106e387efd99a5e4155”,
“7-8bb422162a2e94536f5ef88129060448”,
“6-2e62c7cb537117e11b0143dcd824defc”,
“3-651eb0b7b2bf70972f10714744372bf1”,
“5-44918514d745114627dfa6c5dfbd88a4”,
“1-3bffda9866ff7a99cace376006693e8a”,
“9-5e5c1ff5e2006ef2a7055e3f93e7ba7e”,
“10-ac08f67084a93b63a720e3f1500304fa”,
“2-f865f8d12e4ba966975d29a55152ab04”
],
“parents”: [
2,
4,
3,
5,
9,
1,
-1,
0,
7,
6
],
“channels”: [
[
“all”
],
[
“all”
],
[
“all”
],
[
“all”
],
[
“all”
],
[
“all”
],
[
“all”
],
[
“all”
],
[
“all”
],
[
“all”
]
]
},
“channels”: {
“all”: null
},
“time_saved”: “2017-05-16T21:12:20.20727175Z”
},
… doc body …
}

Here are the revisions which the app is saying are in conflict, but you can see from the server version they are just normal revisions:
(
“CBLSavedRevision[tran…n_es/10-ac08f67084a93b63a720e3f1500304fa]”,
“CBLSavedRevision[tran…n_es/1-3bffda9866ff7a99cace376006693e8a]”
)

Why is couchbase returning this document, and other similar non-conflicting ones, from the conflicts only query if there is actually no conflict? How do we get only documents that have conflicts from the DB?

So there is no way to handle conflicts in the app?

Can you specify the exact sequence of operations on the document? That will help in debugging why the server is reporting the document in conflict.
Also, for testing purposes, can you add a document change observer to the document under test . That way, you can examine the details of every change as and when it happens in the callback and determine when it gets in conflict.

The document has only ever been changed on the server using the sync gateway REST API, because the app is not yet aware of it. This is a translation document that holds translations for a specific language, but we haven’t released that feature to the app yet so the app has no built in handling or parsing of this document type and does not do any operations on it (read, update, delete, etc), it is synced to the app because it is in the all channel but the app does not use it. On the server side we have modified the document nine times after creating it. Each modification was made through Postman using the PUT rest api, where the url was http://<server>/<db name>/<doc id> and then the body was the modified document with _id and _rev fields. Each PUT request was successful. I cannot use a change observer because I am working with a copy of someone else’s local database and the changes were made in the past.

I don’t understand how two different major versions of a document can be reported as in conflict. I thought a conflict was defined as two major versions that are the same but have different hashes, e.g. if a document has version 1-a, 2-a, and 2-b then 2-a and 2-b are in conflict, but 2-a cannot be in conflict with 1-a because 1-a is the parent of 2-a. You can see from the document I posted above that the versions being reported as in conflict are in the same tree, version 1 is the ancestor of version 10, so how can couchbase lite be reporting a version as in conflict with its own ancestor? Is there a way I can see the entire revision tree of a document in couchbase lite that would indicate how a revision can be in conflict with its own ancestor?

Oh, forgot to mention that the server is not involved. The document is not in conflict on the server. Only couchbase lite is returning it in the CBLQuery for allDocs with only conflicts flag set, which means only couchbase lite thinks the document is in conflict. Couchbase lite has never modified the document though.

Well… that’s not entirely true. A conflict is whenever there is a branch in a tree. We loosely depict the point at which the branch occurs as a “conflict”. So for instance, if document has version 1-a, 2-a, 2-b, and 2-b is selected winners and grows to 3-c, 4-d, 5-e . Then the conflicting leaf nodes would be 2-a and 5-e. So the generation ID of conflicting revisions does not have to be the same.
That said, in your example, you should be seeing at least some revisions with sample generation Id to indicate the point of conflict.

Did you try getRevisionHistory on Document?

[quote=“alexegli, post:1, topic:13450”]
Here are the revisions which the app is saying are in conflict, but you can see from the server version they are just normal revisions:
)[/quote]

If it’s the app that complains about a conflict, why are you looking on the server? Check the revision tree in the app’s database.

You’re correct that the server doc doesn’t appear to be in conflict. You can tell a conflict from looking at the parents array, if there are duplicate numbers in it. That indicates either revs with the same parent (a branch) or in the case of -1, a disconnected tree with multiple roots.

Sorry, I was caught in other projects and unable to get back to this till now. I tried getConflictingRevisions to see what the conflicts are, but I’m still confused. Most of the documents report two conflicting revisions, but they are in the same branch in the revision history so I don’t understand how they can be conflicts. Here is an example. This document is never updated by the app, and the server reports no conflicts, here is the _sync object for the doc on the server:
"_sync": {
“rev”: “4-35cb4b13b6c59b73fe346cda00695ddb”,
“sequence”: 13952674,
“recent_sequences”: [
13465785,
13952674
],
“history”: {
“revs”: [
“4-35cb4b13b6c59b73fe346cda00695ddb”,
“1-f01901637df23661a456354895606f9a”,
“2-06c9196b48bbdc5fecf08e64b50e49c7”,
“3-6634019fc09fb56b3d37dee07294b126”
],
“parents”: [
3,
-1,
1,
2
],
“channels”: [
[
“all”
],
[
“all”
],
[
“all”
],
[
“all”
]
]
},
“channels”: {
“all”: null
},
“time_saved”: “2017-05-31T18:24:13.86295731Z”
}

On the app this is what I get when I ran getConflictRevisions and getRevisionHistory on this document:

(lldb)po [document getRevisionHistory:NULL]
<__NSArrayM 0x1630b210>(
CBLSavedRevision[ONEA..BELL/3-6634019fc09fb56b3d37dee07294b126],
CBLSavedRevision[ONEA..BELL/4-35cb4b13b6c59b73fe346cda00695ddb]
)


(lldb) po [document getConflictingRevisions:NULL]
<__NSArrayI 0x1630a470>(
CBLSavedRevision[ONEA..BELL/4-35cb4b13b6c59b73fe346cda00695ddb],
CBLSavedRevision[ONEA..BELL/2-06c9196b48bbdc5fecf08e64b50e49c7]
)

The revision history shows only two revisions, as if it branched at version 2 (or it just got compacted/pruned away, we have default compacting/pruning settings set), and the conflicting revisions shows two versions that the server shows are on the same branch. How is it reporting a conflict between two revisions on the same branch and how can I tell fake conflicts like this from real conflicts in the app?

Well, that’s weird. Thanks for the CBL transcript — the first call shows that the document’s revision history only goes back to revision 3, which means that somehow the ‘parent’ link from 3 to 2 is missing. That’s why 2 appears as a conflict: in CBL’s revision graph, it’s a leaf node.

This looks similar to a revision pruning bug we fixed earlier this year. But that bug only occurs when old revisions get pruned, and the doc you’re showing only has a depth of 4, way less then the default depth limit of 20. (Does SG also have a default max rev tree depth?)

Yes sync gateway also has the default tree depth.

The document has an attachment, not sure if that would affect this. Other
documents without an attachment are having this issue too though. This
document is very old, and probably the last time we updated it was a few
versions ago of sync gateway and couchbase lite. Would that bug you fixed
affect the doc when it was updated, or when the database is querying for
conflicts?

Does this glitch only occur on one specific device (database)? Or can it be reproduced by pulling from the server to an empty database?

If it’s the former, I’m thinking that there have been one or two past bugs in rev-tree management (fixed in 1.3 and 1.4) that might potentially have led to this, although it’s still weird to see it happen with such a shallow rev tree.

I have not been able to reproduce it on an empty database, I just see it on pre-existing databases from current users.

I forgot that one of the documents this is happening too is a relatively new document that has only been in the database for a few weeks, so it has only seen the latest versions of sync gateway and couchbase lite. It also has only about 4 or 5 revisions total, and they were all created by editing on the server, the app does not touch this document.