0

I want to be able to prevent a 'push' (or replication) to the central CouchDB instance from a user's local instance (where there are many users) if there are any conflicts - essentially copying the functionality from something like Git or Mercurial, where users have to resolve conflicts on their local copy before pushing to the central server (so the server can remain 'clean').

As far as I can see, there are two options; preventing a user from committing if there are conflicts (although I can't see any way to do this without actually completing the replication), or completing the replication, seeing if there are any conflicts, and then deleting the pushed replication (and the associated documents) if there are conflicts (though this could also result in undesirable behaviour if a pull is completed before the documents are re-deleted, as well as being a bit inefficient).

So to boil that down, ideally, I would like a replication to roll back if there is a conflict, and to inform me that a conflict has occurred (I don't care about the nature of the conflict, because then I can initiate a pull request replication to replicate the central db on the local db).

Is there any way to achieve either approach that I've missed?

Ian
  • 13
  • 4

2 Answers2

0

You could try to achieve this using a validate_doc_update function. AFAIK you can access the _conflicts property in the new doc to check if there would be some conflicts. But I never tried this before.

Maybe a function like this would work:

function(newDoc, oldDoc, userCtx, secObj) {
  if (newDoc._conflicts && newDoc._conflicts.length) {
    throw({forbidden: 'Your change would cause conflicts. Try resolving the conflicts locally before replicating.'});
  }
}

Just make sure that this validate_doc_update function only exists in a design doc on the central database, because otherwise the users would not be able to "pull" conflicting changes to their local databases.

Please let me know if this works!

Bernhard Gschwantner
  • 1,547
  • 11
  • 12
  • Thanks Bernhard, I'll give it a go and get back to you. The other option I'm working on is to compare the checkpoint_seq of the most recent pull replication by that machine (using the MAC of the machine as part of the replication id), and then checking _changes to identify if there have been subsequent changes. Your solution is prettier though, if it works! – Ian Jun 25 '14 at 08:06
  • 1
    From what I've seen - I called keys(newDoc) and logged the object properties - seems that it only has _id, _rev and _revisions unfortunately. I guess if it is deleted then it might get the additional property _deleted as indicated by the docs. – Ian Jun 25 '14 at 22:10
0

Although it might not be the best solution, I did something a bit more convoluted;

  1. Issue a replication pull request to the central db, specifying the replication id as [RequestingMacAddress].[Guid].
  2. When issuing a replication push request to the central db, I check for the last pull request ID (on the central db), using a custom view (using the same MAC address).
  3. I pull the _changes in descending order, and ensure that the most recent changes on the DB was either the [RequestingMacAddress].[GUID] specified above, or was another pull request (to a different machine), and that no tasks are currently active (e.g. a push replication is in progress from another machine).
  4. If everything is good - allow the push replication to continue, else error out.

Will lookout for any better solutions if available.

Ian
  • 13
  • 4