frantic-team
Version:
Inject replication documents in CouchDB
132 lines (119 loc) • 16 kB
JavaScript
(function() {
// `replicate`
// -----------
var CouchDB, crypto, debug, delay, pkg, replicate, site, sleep, url,
hasProp = {}.hasOwnProperty;
delay = 2000;
// `replicate(source,target,name,extensions)`: replicate database `name` from `source` to `target` (all strings) by creating a replication `pull` document on the target.
// Before submission, the replication document is passed to the (optional) `extensions` callback.
// Returns a Promise. Make sure you `catch()` any errors.
// An optional extra parameter might be used to name a distinct replicator database for storage (supported in CouchDB 2 and above).
module.exports = replicate = async function(prefix_source, prefix_target, name, extensions_cb, group_name = '') {
var _rev, comment, doc, id, k, model, replicator, replicator_db, source, sum, target, use_delete, v;
replicator_db = `${prefix_target}/${group_name}_replicator`;
replicator = new CouchDB(replicator_db);
// Here we have multiple solutions, so I'll test them:
// - either delete any existing document with the same name (this should cancel the replication, based on the CouchDB docs), and recreate a new one;
use_delete = true;
// - or use a different ID for documents that describes different replications.
// use_delete = false
// The one thing we know doesn't work is using the same document ID for documents that describe different replications (e.g. with different filters: experience shows the replicator doesn't notice and keeps using the old filter).
// Deleting the replication document should also force the replicator to stop the existing replication and start a new process.
source = url.parse(prefix_source);
comment = `replication of ${name} from ${source.host}`;
debug(`Going to start ${comment}.`);
// I'm creating a `model` document.. just in case I'd have to revert to manually pushing to `/_replicate` because the replicator is too broken. :)
model = {
comment: comment,
continuous: true,
// Remove authorization from the source and target, because...
target: site(prefix_target, name),
source: site(prefix_source, name)
};
await (typeof extensions_cb === "function" ? extensions_cb(model) : void 0);
// Create a (somewhat) unique ID for the document.
sum = crypto.createHash('sha256');
sum.update(JSON.stringify(model));
id = sum.digest('hex');
model.comment_id = id;
// When deleting, we can use the `comment` value since it doesn't have to be unique even if we change the record.
// When creating documents with different IDs, well, use the computed ID.
model._id = use_delete ? model.comment : id;
// Let's get started.
debug("Going to inject", model);
// Create the target database if it doesn't already exist.
target = new CouchDB(`${prefix_target}/${name}`);
await target.create().catch(function(error) {
// Catch 412 errors as they indicate the database early exists.
if ((error.status != null) && error.status === 412) {
debug("Database already exists");
return;
}
// Report all other errors.
debug.error(`Creating database ${name} failed.`, error);
return Promise.reject(error);
});
target = null;
// When using the deletion method, first delete the existing replication document.
if (use_delete) {
({_rev} = (await replicator.get(model._id).catch(function(error) {
return {};
})));
if (_rev != null) {
await replicator.delete({
_id: model._id,
_rev
});
}
}
// Give CouchDB some time to breath.
await sleep(delay);
// Update the replication document.
({_rev} = (await replicator.get(model._id).catch(function(error) {
return {};
})));
doc = {};
if (_rev != null) {
doc._rev = _rev;
}
for (k in model) {
if (!hasProp.call(model, k)) continue;
v = model[k];
doc[k] = v;
}
debug('Creating replication', doc);
await replicator.update(doc).catch(function(error) {
// Catch 403 errors as they indicate the status was updated by CouchDB (too fast for us to see).
if ((error.status != null) && error.status === 403) {
debug("Replication already started");
return;
}
// Report all other errors.
debug.error(`Replication from ${model.source} for ${model._id} failed.`, error);
return Promise.reject(error);
});
// Give CouchDB some time to breath.
await sleep(delay);
// Log the status of the replicator
doc = (await replicator.get(model._id).catch(function(error) {
return {};
}));
debug('Replication status', doc);
replicator = null;
};
// Toolbox
// =======
CouchDB = require('most-couchdb/with-update');
sleep = function(timeout) {
return new Promise(function(resolve) {
return setTimeout(resolve, timeout);
});
};
crypto = require('crypto');
site = require('frantic-site');
url = require('url');
pkg = require('./package.json');
debug = (require('tangible'))(pkg.name);
}).call(this);
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJpbmRleC5jb2ZmZWUubWQiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7RUFBQTs7QUFBQSxNQUFBLE9BQUEsRUFBQSxNQUFBLEVBQUEsS0FBQSxFQUFBLEtBQUEsRUFBQSxHQUFBLEVBQUEsU0FBQSxFQUFBLElBQUEsRUFBQSxLQUFBLEVBQUEsR0FBQTtJQUFBOztFQUdJLEtBQUEsR0FBUSxLQUhaOzs7Ozs7RUFVSSxNQUFNLENBQUMsT0FBUCxHQUFpQixTQUFBLEdBQVksTUFBQSxRQUFBLENBQUMsYUFBRCxFQUFlLGFBQWYsRUFBNkIsSUFBN0IsRUFBa0MsYUFBbEMsRUFBZ0QsYUFBYSxFQUE3RCxDQUFBO0FBRTNCLFFBQUEsSUFBQSxFQUFBLE9BQUEsRUFBQSxHQUFBLEVBQUEsRUFBQSxFQUFBLENBQUEsRUFBQSxLQUFBLEVBQUEsVUFBQSxFQUFBLGFBQUEsRUFBQSxNQUFBLEVBQUEsR0FBQSxFQUFBLE1BQUEsRUFBQSxVQUFBLEVBQUE7SUFBQSxhQUFBLEdBQWdCLENBQUEsQ0FBQSxDQUFHLGFBQUgsQ0FBaUIsQ0FBakIsQ0FBQSxDQUFvQixVQUFwQixDQUErQixXQUEvQjtJQUNoQixVQUFBLEdBQWEsSUFBSSxPQUFKLENBQVksYUFBWixFQURiOzs7SUFNQSxVQUFBLEdBQWEsS0FOYjs7Ozs7OztJQWVBLE1BQUEsR0FBUyxHQUFHLENBQUMsS0FBSixDQUFVLGFBQVY7SUFDVCxPQUFBLEdBQVUsQ0FBQSxlQUFBLENBQUEsQ0FBa0IsSUFBbEIsQ0FBdUIsTUFBdkIsQ0FBQSxDQUErQixNQUFNLENBQUMsSUFBdEMsQ0FBQTtJQUNWLEtBQUEsQ0FBTSxDQUFBLGVBQUEsQ0FBQSxDQUFrQixPQUFsQixDQUEwQixDQUExQixDQUFOLEVBakJBOztJQXFCQSxLQUFBLEdBQ0U7TUFBQSxPQUFBLEVBQVMsT0FBVDtNQUNBLFVBQUEsRUFBWSxJQURaOztNQUtBLE1BQUEsRUFBUSxJQUFBLENBQUss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LEtBQUssQ0FBQyxHQURELENBRVYsQ0FBQyxLQUZTLENBRUgsUUFBQSxDQUFDLEtBQUQsQ0FBQTthQUFXLENBQUE7SUFBWCxDQUZHLENBQU47SUFJTixLQUFBLENBQU0sb0JBQU4sRUFBNEIsR0FBNUI7SUFDQSxVQUFBLEdBQWE7RUE1SGMsRUFWakM7Ozs7RUE0SUksT0FBQSxHQUFVLE9BQUEsQ0FBUSwwQkFBUjs7RUFFVixLQUFBLEdBQVEsUUFBQSxDQUFDLE9BQUQsQ0FBQTtXQUFhLElBQUksT0FBSixDQUFZLFFBQUEsQ0FBQyxPQUFELENBQUE7YUFBYSxVQUFBLENBQVcsT0FBWCxFQUFvQixPQUFwQjtJQUFiLENBQVo7RUFBYjs7RUFDUixNQUFBLEdBQVMsT0FBQSxDQUFRLFFBQVI7O0VBQ1QsSUFBQSxHQUFPLE9BQUEsQ0FBUSxjQUFSOztFQUNQLEdBQUEsR0FBTSxPQUFBLENBQVEsS0FBUjs7RUFDTixHQUFBLEdBQU0sT0FBQSxDQUFRLGdCQUFSOztFQUNOLEtBQUEsR0FBUSxDQUFDLE9BQUEsQ0FBUSxVQUFSLENBQUQsQ0FBQSxDQUFxQixHQUFHLENBQUMsSUFBekI7QUFuSloiLCJzb3VyY2VzQ29udGVudCI6WyJgcmVwbGljYXRlYFxuLS0tLS0tLS0tLS1cblxuICAgIGRlbGF5ID0gMjAwMFxuXG5gcmVwbGljYXRlKHNvdXJjZSx0YXJnZXQsbmFtZSxleHRlbnNpb25zKWA6IHJlcGxpY2F0ZSBkYXRhYmFzZSBgbmFtZWAgZnJvbSBgc291cmNlYCB0byBgdGFyZ2V0YCAoYWxsIHN0cmluZ3MpIGJ5IGNyZWF0aW5nIGEgcmVwbGljYXRpb24gYHB1bGxgIGRvY3VtZW50IG9uIHRoZSB0YXJnZXQuXG5CZWZvcmUgc3VibWlzc2lvbiwgdGhlIHJlcGxpY2F0aW9uIGRvY3VtZW50IGlzIHBhc3NlZCB0byB0aGUgKG9wdGlvbmFsKSBgZXh0ZW5zaW9uc2AgY2FsbGJhY2suXG5SZXR1cm5zIGEgUHJvbWlzZS4gTWFrZSBzdXJlIHlvdSBgY2F0Y2goKWAgYW55IGVycm9ycy5cbkFuIG9wdGlvbmFsIGV4dHJhIHBhcmFtZXRlciBtaWdodCBiZSB1c2VkIHRvIG5hbWUgYSBkaXN0aW5jdCByZXBsaWNhdG9yIGRhdGFiYXNlIGZvciBzdG9yYWdlIChzdXBwb3J0ZWQgaW4gQ291Y2hEQiAyIGFuZCBhYm92ZSkuXG5cbiAgICBtb2R1bGUuZXhwb3J0cyA9IHJlcGxpY2F0ZSA9IChwcmVmaXhfc291cmNlLHByZWZpeF90YXJnZXQsbmFtZSxleHRlbnNpb25zX2NiLGdyb3VwX25hbWUgPSAnJykgLT5cblxuICAgICAgcmVwbGljYXRvcl9kYiA9IFwiI3twcmVmaXhfdGFyZ2V0fS8je2dyb3VwX25hbWV9X3JlcGxpY2F0b3JcIlxuICAgICAgcmVwbGljYXRvciA9IG5ldyBDb3VjaERCIHJlcGxpY2F0b3JfZGJcblxuSGVyZSB3ZSBoYXZlIG11bHRpcGxlIHNvbHV0aW9ucywgc28gSSdsbCB0ZXN0IHRoZW06XG4tIGVpdGhlciBkZWxldGUgYW55IGV4aXN0aW5nIGRvY3VtZW50IHdpdGggdGhlIHNhbWUgbmFtZSAodGhpcyBzaG91bGQgY2FuY2VsIHRoZSByZXBsaWNhdGlvbiwgYmFzZWQgb24gdGhlIENvdWNoREIgZG9jcyksIGFuZCByZWNyZWF0ZSBhIG5ldyBvbmU7XG5cbiAgICAgIHVzZV9kZWxldGUgPSB0cnVlXG5cbi0gb3IgdXNlIGEgZGlmZmVyZW50IElEIGZvciBkb2N1bWVudHMgdGhhdCBkZXNjcmliZXMgZGlmZmVyZW50IHJlcGxpY2F0aW9ucy5cblxuICAgICAgIyB1c2VfZGVsZXRlID0gZmFsc2VcblxuVGhlIG9uZSB0aGluZyB3ZSBrbm93IGRvZXNuJ3Qgd29yayBpcyB1c2luZyB0aGUgc2FtZSBkb2N1bWVudCBJRCBmb3IgZG9jdW1lbnRzIHRoYXQgZGVzY3JpYmUgZGlmZmVyZW50IHJlcGxpY2F0aW9ucyAoZS5nLiB3aXRoIGRpZmZlcmVudCBmaWx0ZXJzOiBleHBlcmllbmNlIHNob3dzIHRoZSByZXBsaWNhdG9yIGRvZXNuJ3Qgbm90aWNlIGFuZCBrZWVwcyB1c2luZyB0aGUgb2xkIGZpbHRlcikuXG5EZWxldGluZyB0aGUgcmVwbGljYXRpb24gZG9jdW1lbnQgc2hvdWxkIGFsc28gZm9yY2UgdGhlIHJlcGxpY2F0b3IgdG8gc3RvcCB0aGUgZXhpc3RpbmcgcmVwbGljYXRpb24gYW5kIHN0YXJ0IGEgbmV3IHByb2Nlc3MuXG5cbiAgICAgIHNvdXJjZSA9IHVybC5wYXJzZSBwcmVmaXhfc291cmNlXG4gICAgICBjb21tZW50ID0gXCJyZXBsaWNhdGlvbiBvZiAje25hbWV9IGZyb20gI3tzb3VyY2UuaG9zdH1cIlxuICAgICAgZGVidWcgXCJHb2luZyB0byBzdGFydCAje2NvbW1lbnR9LlwiXG5cbkknbSBjcmVhdGluZyBhIGBtb2RlbGAgZG9jdW1lbnQuLiBqdXN0IGluIGNhc2UgSSdkIGhhdmUgdG8gcmV2ZXJ0IHRvIG1hbnVhbGx5IHB1c2hpbmcgdG8gYC9fcmVwbGljYXRlYCBiZWNhdXNlIHRoZSByZXBsaWNhdG9yIGlzIHRvbyBicm9rZW4uIDopXG5cbiAgICAgIG1vZGVsID1cbiAgICAgICAgY29tbWVudDogY29tbWVudFxuICAgICAgICBjb250aW51b3VzOiB0cnVlXG5cblJlbW92ZSBhdXRob3JpemF0aW9uIGZyb20gdGhlIHNvdXJjZSBhbmQgdGFyZ2V0LCBiZWNhdXNlLi4uXG5cbiAgICAgICAgdGFyZ2V0OiBzaXRlIHByZWZpeF90YXJnZXQsIG5hbWVcbiAgICAgICAgc291cmNlOiBzaXRlIHByZWZpeF9zb3VyY2UsIG5hbWVcblxuTGV0IHRoZSBjYWxsYmFjayBhZGQgYW55IGZpZWxkIHRoZXknZCBsaWtlLlxuTm90ZTogdGhlIGNhbGxiYWNrIG1pZ2h0IGFsc28gcHJldmVudCByZXBsaWNhdGlvbiBpZiBpdCB0aHJvd3MuIFRoaXMgaXMgaW50ZW50aW9uYWwuXG5Ob3RlOiB0aGUgY2FsbGJhY2sgbWlnaHQgcmV0dXJuIGEgUHJvbWlzZS4gT3Igbm90LiBXZSdsbCBkZWFsIHdpdGggYm90aC5cblxuICAgICAgYXdhaXQgZXh0ZW5zaW9uc19jYj8gbW9kZWxcblxuQ3JlYXRlIGEgKHNvbWV3aGF0KSB1bmlxdWUgSUQgZm9yIHRoZSBkb2N1bWVudC5cblxuICAgICAgc3VtID0gY3J5cHRvLmNyZWF0ZUhhc2ggJ3NoYTI1NidcbiAgICAgIHN1bS51cGRhdGUgSlNPTi5zdHJpbmdpZnkgbW9kZWxcbiAgICAgIGlkID0gc3VtLmRpZ2VzdCAnaGV4J1xuICAgICAgbW9kZWwuY29tbWVudF9pZCA9IGlkXG5cbldoZW4gZGVsZXRpbmcsIHdlIGNhbiB1c2UgdGhlIGBjb21tZW50YCB2YWx1ZSBzaW5jZSBpdCBkb2Vzbid0IGhhdmUgdG8gYmUgdW5pcXVlIGV2ZW4gaWYgd2UgY2hhbmdlIHRoZSByZWNvcmQuXG5XaGVuIGNyZWF0aW5nIGRvY3VtZW50cyB3aXRoIGRpZmZlcmVudCBJRHMsIHdlbGwsIHVzZSB0aGUgY29tcHV0ZWQgSUQuXG5cbiAgICAgIG1vZGVsLl9pZCA9IGlmIHVzZV9kZWxldGUgdGhlbiBtb2RlbC5jb21tZW50IGVsc2UgaWRcblxuTGV0J3MgZ2V0IHN0YXJ0ZWQuXG5cbiAgICAgIGRlYnVnIFwiR29pbmcgdG8gaW5qZWN0XCIsIG1vZGVsXG5cbkNyZWF0ZSB0aGUgdGFyZ2V0IGRhdGFiYXNlIGlmIGl0IGRvZXNuJ3QgYWxyZWFkeSBleGlzdC5cblxuICAgICAgdGFyZ2V0ID0gbmV3IENvdWNoREIgXCIje3ByZWZpeF90YXJnZXR9LyN7bmFtZX1cIlxuICAgICAgYXdhaXQgdGFyZ2V0LmNyZWF0ZSgpXG4gICAgICAgIC5jYXRjaCAoZXJyb3IpIC0+XG5cbkNhdGNoIDQxMiBlcnJvcnMgYXMgdGhleSBpbmRpY2F0ZSB0aGUgZGF0YWJhc2UgZWFybHkgZXhpc3RzLlxuXG4gICAgICAgICAgaWYgZXJyb3Iuc3RhdHVzPyBhbmQgZXJyb3Iuc3RhdHVzIGlzIDQxMlxuICAgICAgICAgICAgZGVidWcgXCJEYXRhYmFzZSBhbHJlYWR5IGV4aXN0c1wiXG4gICAgICAgICAgICByZXR1cm5cblxuUmVwb3J0IGFsbCBvdGhlciBlcnJvcnMuXG5cbiAgICAgICAgICBkZWJ1Zy5lcnJvciBcIkNyZWF0aW5nIGRhdGFiYXNlICN7bmFtZX0gZmFpbGVkLlwiLCBlcnJvclxuICAgICAgICAgIFByb21pc2UucmVqZWN0IGVycm9yXG5cbiAgICAgIHRhcmdldCA9IG51bGxcblxuV2hlbiB1c2luZyB0aGUgZGVsZXRpb24gbWV0aG9kLCBmaXJzdCBkZWxldGUgdGhlIGV4aXN0aW5nIHJlcGxpY2F0aW9uIGRvY3VtZW50LlxuXG4gICAgICBpZiB1c2VfZGVsZXRlXG4gICAgICAgIHtfcmV2fSA9IGF3YWl0IHJlcGxpY2F0b3JcbiAgICAgICAgICAuZ2V0IG1vZGVsLl9pZFxuICAgICAgICAgIC5jYXRjaCAoZXJyb3IpIC0+IHt9XG4gICAgICAgIGF3YWl0IHJlcGxpY2F0b3IuZGVsZXRlIHtfaWQ6bW9kZWwuX2lkLCBfcmV2fSBpZiBfcmV2P1xuXG5HaXZlIENvdWNoREIgc29tZSB0aW1lIHRvIGJyZWF0aC5cblxuICAgICAgYXdhaXQgc2xlZXAgZGVsYXlcblxuVXBkYXRlIHRoZSByZXBsaWNhdGlvbiBkb2N1bWVudC5cblxuICAgICAge19yZXZ9ID0gYXdhaXQgcmVwbGljYXRvclxuICAgICAgICAuZ2V0IG1vZGVsLl9pZFxuICAgICAgICAuY2F0Y2ggKGVycm9yKSAtPiB7fVxuXG4gICAgICBkb2MgPSB7fVxuICAgICAgZG9jLl9yZXYgPSBfcmV2IGlmIF9yZXY/XG4gICAgICBmb3Igb3duIGssdiBvZiBtb2RlbFxuICAgICAgICBkb2Nba10gPSB2XG5cbiAgICAgIGRlYnVnICdDcmVhdGluZyByZXBsaWNhdGlvbicsIGRvY1xuXG4gICAgICBhd2FpdCByZXBsaWNhdG9yXG4gICAgICAgIC51cGRhdGUgZG9jXG4gICAgICAgIC5jYXRjaCAoZXJyb3IpIC0+XG5cbkNhdGNoIDQwMyBlcnJvcnMgYXMgdGhleSBpbmRpY2F0ZSB0aGUgc3RhdHVzIHdhcyB1cGRhdGVkIGJ5IENvdWNoREIgKHRvbyBmYXN0IGZvciB1cyB0byBzZWUpLlxuXG4gICAgICAgICAgaWYgZXJyb3Iuc3RhdHVzPyBhbmQgZXJyb3Iuc3RhdHVzIGlzIDQwM1xuICAgICAgICAgICAgZGVidWcgXCJSZXBsaWNhdGlvbiBhbHJlYWR5IHN0YXJ0ZWRcIlxuICAgICAgICAgICAgcmV0dXJuXG5cblJlcG9ydCBhbGwgb3RoZXIgZXJyb3JzLlxuXG4gICAgICAgICAgZGVidWcuZXJyb3IgXCJSZXBsaWNhdGlvbiBmcm9tICN7bW9kZWwuc291cmNlfSBmb3IgI3ttb2RlbC5faWR9IGZhaWxlZC5cIiwgZXJyb3JcbiAgICAgICAgICBQcm9taXNlLnJlamVjdCBlcnJvclxuXG5HaXZlIENvdWNoREIgc29tZSB0aW1lIHRvIGJyZWF0aC5cblxuICAgICAgYXdhaXQgc2xlZXAgZGVsYXlcblxuTG9nIHRoZSBzdGF0dXMgb2YgdGhlIHJlcGxpY2F0b3JcblxuICAgICAgZG9jID0gYXdhaXQgcmVwbGljYXRvclxuICAgICAgICAuZ2V0IG1vZGVsLl9pZFxuICAgICAgICAuY2F0Y2ggKGVycm9yKSAtPiB7fVxuXG4gICAgICBkZWJ1ZyAnUmVwbGljYXRpb24gc3RhdHVzJywgZG9jXG4gICAgICByZXBsaWNhdG9yID0gbnVsbFxuICAgICAgcmV0dXJuXG5cblRvb2xib3hcbj09PT09PT1cblxuICAgIENvdWNoREIgPSByZXF1aXJlICdtb3N0LWNvdWNoZGIvd2l0aC11cGRhdGUnXG5cbiAgICBzbGVlcCA9ICh0aW1lb3V0KSAtPiBuZXcgUHJvbWlzZSAocmVzb2x2ZSkgLT4gc2V0VGltZW91dCByZXNvbHZlLCB0aW1lb3V0XG4gICAgY3J5cHRvID0gcmVxdWlyZSAnY3J5cHRvJ1xuICAgIHNpdGUgPSByZXF1aXJlICdmcmFudGljLXNpdGUnXG4gICAgdXJsID0gcmVxdWlyZSAndXJsJ1xuICAgIHBrZyA9IHJlcXVpcmUgJy4vcGFja2FnZS5qc29uJ1xuICAgIGRlYnVnID0gKHJlcXVpcmUgJ3RhbmdpYmxlJykgcGtnLm5hbWVcbiJdfQ==
//# sourceURL=/dev/shm/frantic-team/index.coffee.md