Created
January 1, 2014 13:31
-
-
Save ingorammer/8208056 to your computer and use it in GitHub Desktop.
(Addendum to https://github.com/daleharvey/pouchdb/issues/1201) I've created this gist to detail some PouchDB Local->Remote issues which I've noticed with continuous replication when resolving conflicts (i.e. when remove()-ing the conflicting revisions after performing some app-specific conflict resolution magic).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
For continuous replication, the following POSTs are sent to _revs_diff and _bulk_docs: | |
POST to _revs_diff: | |
{ | |
"foo": ["3-c"] | |
} | |
RESPONSE: | |
{ | |
"foo": { | |
"missing": ["3-c"], | |
"possible_ancestors": ["2-b", "2-c"] | |
} | |
} | |
Resulting in the following POST to _bulk_docs: | |
{ | |
"docs": [{ | |
"value": "db1", | |
"_id": "foo", | |
"_rev": "3-c", | |
"_revisions": { | |
"start": 2, | |
"ids": ["c", "a"] | |
} | |
}], | |
"new_edits": false | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
For manual replication, the following POSTs are sent to _revs_diff and _bulk_docs: | |
POST to _revs_diff: | |
{ | |
"foo": ["3-5e51174f2ba73f498b3689beb8364276", "3-c"] | |
} | |
RESPONSE: | |
{ | |
"foo": { | |
"missing": ["3-5e51174f2ba73f498b3689beb8364276", "3-c"], | |
"possible_ancestors": ["2-b", "2-c"] | |
} | |
} | |
Resulting in the following POST to _bulk_docs: | |
{ | |
"docs": [{ | |
"_id": "foo", | |
"_rev": "3-5e51174f2ba73f498b3689beb8364276", | |
"_deleted": true, | |
"_revisions": { | |
"start": 3, | |
"ids": ["5e51174f2ba73f498b3689beb8364276", "b", "a"] | |
} | |
}, { | |
"value": "db1", | |
"_id": "foo", | |
"_rev": "3-c", | |
"_revisions": { | |
"start": 2, | |
"ids": ["c", "a"] | |
} | |
}], | |
"new_edits": false | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
"use strict"; | |
var adapters = [ | |
['local-1', 'http-1'], | |
['http-1', 'local-1'] | |
]; | |
if (typeof module !== undefined && module.exports) { | |
var PouchDB = require('../lib'); | |
var testUtils = require('./test.utils.js'); | |
} | |
adapters.map(function (adapters) { | |
QUnit.module('continuous replication with conflict resolution: ' + adapters[0] + ':' + adapters[1], { | |
setup: function () { | |
this.name = testUtils.generateAdapterUrl(adapters[0]); | |
this.remote = testUtils.generateAdapterUrl(adapters[1]); | |
PouchDB.enableAllDbs = true; | |
}, | |
teardown: testUtils.cleanupTestDatabases | |
}); | |
asyncTest('Testing conflict resolution with subsequent manual unidirectional replication', function () { | |
// we indeed needed replication to create failing test here! | |
testUtils.initDBPair(this.name, this.remote, function (db1, db2) { | |
var doc = { | |
_id: "foo", | |
_rev: "1-a", | |
value: "generic" | |
}; | |
db1.put(doc, {new_edits: false}, function (err, res) { | |
db2.put(doc, {new_edits: false}, function (err, res) { | |
testUtils.putAfter(db2, {_id: "foo", _rev: "2-b", value: "db2"}, "1-a", function (err, res) { | |
testUtils.putAfter(db1, {_id: "foo", _rev: "2-c", value: "whatever"}, "1-a", function (err, res) { | |
testUtils.putAfter(db1, {_id: "foo", _rev: "3-c", value: "db1"}, "2-c", function (err, res) { | |
db1.get("foo", {conflicts: true}, function (err, doc) { | |
ok(doc.value === "db1", "db1 has correct value (get)"); | |
ok(!doc._conflicts || doc._conflicts.length === 0, "db1 has no conflicts before replication"); | |
db2.get("foo", function (err, doc) { | |
ok(doc.value === "db2", "db2 has correct value (get)"); | |
ok(!doc._conflicts || doc._conflicts.length === 0, "db2 sees no conflicts before replication"); | |
PouchDB.replicate(db1, db2, function () { | |
PouchDB.replicate(db2, db1, function () { | |
db1.get("foo", {conflicts: true}, function (err, doc) { | |
ok(doc.value === "db1", "db1 has correct value (get after replication)"); | |
ok(doc._conflicts && doc._conflicts.length === 1, "db1 sees conflict after replication"); | |
var conflictRev = doc._conflicts[0]; | |
db2.get("foo", {conflicts: true}, function (err, doc) { | |
ok(doc.value === "db1", "db2 has correct value (get after replication)"); | |
ok(doc._conflicts && doc._conflicts.length === 1, "db2 sees conflict after replication"); | |
db1.remove({_id: "foo", _rev: conflictRev}, function (err, res) { | |
ok(res.ok, "Conflicting document removed on db1"); | |
db1.get("foo", {conflicts: true}, function (err, doc) { | |
ok(!doc._conflicts || doc._conflicts.length === 0, "db1 sees no more conflicts after removing it"); | |
PouchDB.replicate(db1, db2, function () { | |
db1.get("foo", {conflicts: true}, function (err, doc) { | |
ok(doc.value === "db1", "db1 has correct value (get after replication)"); | |
ok(!doc._conflicts || doc._conflicts.length === 0, "db1 sees no conflict after replication"); | |
console.log("DB1 document"); | |
console.dir(doc); | |
db2.get("foo", {conflicts: true}, function (err, doc) { | |
ok(doc.value === "db1", "db2 has correct value (get after replication)"); | |
ok(!doc._conflicts || doc._conflicts.length === 0, "db2 sees no conflict after replication"); | |
console.log("DB2 document"); | |
console.dir(doc); | |
start(); | |
}); | |
}); | |
}); | |
}); | |
}); | |
}); | |
}); | |
}); | |
}); | |
}); | |
}); | |
}); | |
}); | |
}); | |
}); | |
}); | |
}); | |
}); | |
asyncTest('Testing conflict resolution with continuous replication', function () { | |
// we indeed needed replication to create failing test here! | |
testUtils.initDBPair(this.name, this.remote, function (db1, db2) { | |
var doc = { | |
_id: "foo", | |
_rev: "1-a", | |
value: "generic" | |
}; | |
db1.put(doc, {new_edits: false}, function (err, res) { | |
db2.put(doc, {new_edits: false}, function (err, res) { | |
testUtils.putAfter(db2, {_id: "foo", _rev: "2-b", value: "db2"}, "1-a", function (err, res) { | |
testUtils.putAfter(db1, {_id: "foo", _rev: "2-c", value: "whatever"}, "1-a", function (err, res) { | |
testUtils.putAfter(db1, {_id: "foo", _rev: "3-c", value: "db1"}, "2-c", function (err, res) { | |
db1.get("foo", {conflicts: true}, function (err, doc) { | |
ok(doc.value === "db1", "db1 has correct value (get)"); | |
ok(!doc._conflicts || doc._conflicts.length === 0, "db1 has no conflicts before replication"); | |
db2.get("foo", function (err, doc) { | |
ok(doc.value === "db2", "db2 has correct value (get)"); | |
ok(!doc._conflicts || doc._conflicts.length === 0, "db2 sees no conflicts before replication"); | |
PouchDB.replicate(db1, db2, function () { | |
PouchDB.replicate(db2, db1, function () { | |
db1.get("foo", {conflicts: true}, function (err, doc) { | |
ok(doc.value === "db1", "db1 has correct value (get after replication)"); | |
ok(doc._conflicts && doc._conflicts.length === 1, "db1 sees conflict after replication"); | |
var conflictRev = doc._conflicts[0]; | |
console.log("LOCAL BEFORE SYNC"); | |
console.dir(doc); | |
db2.get("foo", {conflicts: true}, function (err, doc) { | |
console.log("REMOTE BEFORE SYNC"); | |
console.dir(doc); | |
ok(doc.value === "db1", "db2 has correct value (get after replication)"); | |
ok(doc._conflicts && doc._conflicts.length === 1, "db2 sees conflict after replication"); | |
var initialChangeComplete = false; | |
function continueAfterFirstSync() { | |
console.log("Deleting"); | |
db1.remove({_id: "foo", _rev: conflictRev}, function (err, res) { | |
ok(res.ok, "Conflicting document removed on db1"); | |
db1.get("foo", {conflicts: true}, function (err, doc) { | |
ok(!doc._conflicts || doc._conflicts.length === 0, "db1 sees no more conflicts after removing it"); | |
setTimeout(function () { | |
console.log("Resuming after wait ...") | |
db2.get("foo", {conflicts: true}, function (err, doc) { | |
ok(doc.value === "db1", "db2 has correct value (get after replication)"); | |
ok(!doc._conflicts || doc._conflicts.length === 0, "db2 sees no conflict after replication"); | |
console.log("DB2 document"); | |
console.dir(doc); | |
start(); | |
}); | |
}, 1000); | |
}); | |
}); | |
} | |
PouchDB.replicate(db1, db2, { | |
continuous: true, | |
onChange: function (status) { | |
console.log("==== SYNC CHANGES ===="); | |
console.dir(status); | |
}}); | |
// continue with change watching after a couple of milliseconds to allow for first sync for local->remote ... | |
setTimeout(function () { | |
continueAfterFirstSync(); | |
}, 500); | |
}); | |
}); | |
}); | |
}); | |
}); | |
}); | |
}); | |
}); | |
}); | |
}); | |
}); | |
}); | |
}); | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment