How to keep just server revision during conflicts?


#1

In my android app i have setup pull replication with sync_gateway.

Due to the specific app’s logic, whenever a conflict is detected, i need to remove any other info and keep just the ones replicated from the server version.

I’m able to define which conflicting version comes from the replication and which one comes from a local modification and i want to keep the server revision. If i create tombstones and new revision with the server data, i will have continuos conflicts because id of local revision will be always different from the one on the server.

I need to have a local object that is an exact copy of the remote one so that it will never produce conflicts until real ones will be generated by local modifications.

Is it possible to purge revisions? Or is it possibile to create an object that contains just the server revision? I need a behaviour similar to an override.


#2

Just delete the local revision. The server revision will remain.


#3

But how can i delete a local revision?

Maybe this is something i don’t understand but to delete a local revision i have to create a new one and set deletion to true. This operation will produce a new revision with a new revision number greater than the previous one. For what i’ve understood, it is impossible to delete a revision. You can only create a new one which is a deletion revision. Am i wrong?

As an example: server creates rev 1-111 and pull replication replicates it to mobile. On mobile i create rev 2-333. After a while a new revision is created on server (rev 2-222) and replicated to mobile. So there is a conflict on mobile but the winning rev is the mobile rev 2-333 due to its greater rev number.

If i decide to delete rev 2-333 (which is exactly what i would like to do) i can’t remove or purge that local revision, but i must create a new revision (i.e. rev 3-111) where deletion is set to true (and this is a tombstone revision). But if i do this operation, the new winning revision will become rev 3-111 because of the greater rev number so the result will be that i have deleted the document!

I’m really struggling to understand how can i delete a revision without creating a new one. I even tried to purge a revision but it doesn’t seem to work.

Can you provide me a simple snippet of code (android) where you show me how can i delete a local revision without creating a new one? Please help me, i’m really struggling!


#4

3-111 will not become the winning revision. Deleted revisions cannot win over non-deleted revisions, even if their revision number is higher. Think of this like git branches. Each git branch has a HEAD, as does each revision branch in your revision history. Deleting a revision is like deleting a branch in git, the HEAD will simply go somewhere else. So to follow your example:

You create rev 1-111. This is the current revision because it is the non-deleted revision with the highest generation. Then you create rev 2-333 on mobile, this becomes the new winning revision for the same reason. Then you pull rev 2-222 from the server. Now you have two non-deleted current revisions (aka a conflict). In this case, 2-333 is still the winning revision by virtue of its revision hash being sorted higher as a string (arbitrary, but we can’t do anything else without specific knowledge of a use case). So, then you create 3-111, which is a deletion. 2-333 becomes non-current because it is not the newest revision in the branch (aka not the HEAD). However, 3-111 is a deletion and cannot become the winning revision and so it falls to the next current revision (which is 2-222, the new highest non-deleted HEAD).

So really, 3-111 is not only not going to cause trouble, it is essential. We cannot remove revisions without breaking MVCC. We need a way to communicate to other endpoints that a revision has been deleted (aka a tombstone). The other clients will follow the same algorithm and consider 2-222 the new winning revision.


#5

Borrrden thank you so much for your answer.
It seems to me that it contradicts what i’ve verified, but maybe i have done a mess so i will double check.

But there is something that is not clear to me with what you said: if i decide to explicitly delete a document ( document.delete() ), a new deletion revision is created and that is the winning revision and this seems to contradict what you said:

Deleted revisions cannot win over non-deleted revisions, even if their revision number is higher

Am i wrong?

Just to completely understand, when you say delete a revision, do you mean this?

SavedRevision current = doc.getCurrentRevision();
UnsavedRevision newRev = current.createRevision();
newRev.setIsDeletion(true);
newRev.save(true);

Thank you for any help.


#6

If you have no conflicts and you delete a document, you will get a deleted document (one with no winning revisions). If you create a conflict and

A) You call doc.Delete(), it will delete the winning revision and the non-winning revision will come current (if there is one).

B) You set IsDeletion on an UnsavedRevision and save it, it will delete that revision and the other one will become the winning revision.


#7

Dear borrrden,
i apologize: every thing works exactly as you said, you are right

I can’t understand and remember why i got confused and why i was so sure that things worked as i wrote.

Maybe i was confused by the conflict situation. So to recap:

  • When you have conflicts it means that there are open branches in the revision tree (and with open branch i mean a revision branch that doesn’t have a tombstone at its end).
  • If you don’t have conflicts it means that you may have different revision branches but all except one have a tombstone revision at their end. The one without tombstone is the current (winning) revision
  • If all the branches have a tombstone that means you have deleted the document
  • When an open branch that represents the current (winning) revision gets closed with a tombstone revision, another open branch is elected as current (winning) revision. If there are no other open branches then the document is considered deleted
  • When an open branch that doesn’t represents the current (winning) revision (and thus generates conflicts) gets closed with a tombstone revision, nothing changes except that that branch won’t create conflicts anymore

#8

The rules are a bit complex, though they work well in practice. Basically:

  • If there are two or more non-deleted leaf revisions, there is a conflict.
  • In case of a conflict, the default (“winning”) revision is the deepest non-deleted one, i.e. the one with the largest numeric prefix.
  • If there’s a tie, the revision with the highest revID (by a plain ASCII comparison, i.e. strcmp) wins. This is obviously completely arbitrary, but it does produce a single deterministic winner that all clients agree on.

#9

Thank you very much borrrden and jens, your explanations were very useful.

I think your two answers and the example with revs number should be added to the docs as your answers were really clarifying!

There is just a strange edge-case: i created a document with id “x_15” on the mobile app. Then i edited the document locally (revs goes up to 2-xyz). I then created a doc with same id on the server side. When pull replication got executed, i would expect a conflict on the doc as the initial server revision was different, but the document wasn’t considered changed (i have a database listener for changes). Is it a correct behaviour?

Thank you very much.


#10

Actually I found a very similar explanation in the Conflicts section of the docs. Had you seen that before?

i would expect a conflict on the doc as the initial server revision was different, but the document wasn’t considered changed (i have a database listener for changes). Is it a correct behaviour?

The document’s default revision won’t change, since the local revision is deeper (generation 2.) I can’t remember whether a database-change notification is triggered in this case; I’d have to go look at the code. It does seem like it should be, since you may want to be notified of conflicts. File an issue about this and we can look into it.


#11

I checked my code. Document change is notified. The problem is that documentChange.isConflict() is false. But if i ask for the conflicting revisions with document.getConflictingRevisions() i get exactly the server revision 1-xyz and the local 2-qwe rev.

The problem with my code is that i execute conflict resolutions only if documentChange.isConflict() is true. Is this a bug or am i missing something?


#12

Sounds like a bug. Please file a bug report on Github!