How to resolve conflicts with continuous replication
Asked Answered
B

1

13

I'm new to both CouchDB and PouchDB and am using it to create a contact management system that syncs across mobile and desktop devices, and can be used offline. I am seeing that using PouchDB is infinitely easier than having to write a PHP/MySQL backend.

I have been using it successfully, and when I make conflicting changes on offline devices, CouchDB uses an algorithm to arbitrarily pick a winner and then correctly pushes it to all the devices.

What I would like to do is implement a custom algorithm to merge conflicting records. Here is the algorithm I would like to use:

  1. If a record is deleted on one client and merely updated on another, the updated version wins, unless both clients agree on the delete.
  2. The record with the most recent "modified" timestamp becomes the master, and the older record becomes the secondary.
  3. Any fields that exist only in the secondary (or are empty in the master) are moved over to the master.
  4. The master revision is saved and the secondary is deleted.

CouchDB's guide has a good explanation, but I don't have a clue how to implement it with the PouchDB API during a continuous replication. According to the PouchDB API, there is an "onChange" listener in the replicate options, but I don't understand how to use it to intercept conflicts.

If someone could write a brief tutorial including some sample code, myself and I'm sure many other PouchDB users would appreciate it!

Beechnut answered 25/8, 2013 at 23:16 Comment(0)
D
12

Writing an article with examples of exactly how to manage conflict resolution is a really good idea, it can be confusing, but with the lack of one

The idea is the exact same as CouchDB, to resolve conflicts you delete the revisions that didnt win (and write a new winner if needed)

#1 is how CouchDB conflict resolution works anyway, so you dont need to worry about that, deleted leafs dont conflict

function onChange(doc) { 
  if (!doc._conflicts) return;
    collectConflicts(doc._conflicts, function(docs) {
      var master = docs.sort(byTime).unshift();
      for (var doc in docs) {
        for (var prop in doc) {
          if (!(prop in master)) { 
            master[prop] = doc[prop];
          } 
        }
      }
      db.put(master, function(err) {
        if (!err) { 
          for (var doc in docs) {
            db.remove(doc);
          }
        }
      });     
    });
  }
}

db.changes({conflicts: true, onChange: onChange});

This will need error handling etc and could be written much nicer, was just a quick napkin drawing of what the code could look like

Disavow answered 18/9, 2013 at 9:2 Comment(1)
Thank you very much Dale! This helps a lot.Beechnut

© 2022 - 2024 — McMap. All rights reserved.