diff --git a/scripts/doc.js b/scripts/doc.js index a1c9631..601eb89 100644 --- a/scripts/doc.js +++ b/scripts/doc.js @@ -2,7 +2,8 @@ var promisedRequest = require('./request'), CouchPersistentStreamIterator = require('./couch-persistent-stream-iterator'), - sporks = require('sporks'); + sporks = require('sporks'), + Backoff = require('backoff-promise'); var Doc = function (slouch) { this._slouch = slouch; @@ -159,27 +160,33 @@ Doc.prototype.createOrUpdateIgnoreConflict = function (dbName, doc) { }); }; +// Provide a construct for mocking +Doc.prototype._newBackoff = function () { + return new Backoff(); +}; + Doc.prototype._persistThroughConflicts = function (promiseFactory) { var self = this, i = 0; + // Use an exponential backoff to prevent multiple ticks from competing with each other and + // resulting in none of the ticks persisting through the conflict within the allotted number of + // retries. + var backoff = self._newBackoff(); + var run = function () { - return promiseFactory().catch(function (err) { - - if (err.error === 'conflict' && i++ < self.maxRetries) { // conflict? - - // Retry + return backoff.attempt(function () { + return promiseFactory(); + }).catch(function (err) { + // Conflict and haven't reached max retries? + if (err.error === 'conflict' && i++ < self.maxRetries) { + // Attempt again return run(); - } else { - - // Unexpected error throw err; - } - }); }; diff --git a/test/spec/doc.js b/test/spec/doc.js index 7112370..44fd3c4 100644 --- a/test/spec/doc.js +++ b/test/spec/doc.js @@ -4,7 +4,8 @@ var Slouch = require('../../scripts'), utils = require('../utils'), sporks = require('sporks'), Promise = require('sporks/scripts/promise'), - config = require('../config.json'); + config = require('../config.json'), + Backoff = require('backoff-promise'); describe('doc', function () { @@ -19,6 +20,12 @@ describe('doc', function () { slouch = new Slouch(utils.couchDBURL()); db = slouch.db; updates = []; + + // Shorten backoff + slouch.doc._newBackoff = function () { + return new Backoff(1); + }; + return utils.createDB(); }); @@ -265,8 +272,6 @@ describe('doc', function () { }); it('upsert should fail after max retries', function () { - slouch.maxRetries = 3; - // Disable for conflict faking slouch.doc.ignoreDuplicateUpdates = false; @@ -347,8 +352,6 @@ describe('doc', function () { }); it('get, merge and upsert should fail after max retries', function () { - slouch.maxRetries = 3; - // Disable for conflict faking slouch.doc.ignoreDuplicateUpdates = false; @@ -382,8 +385,6 @@ describe('doc', function () { }); it('get, modify and upsert should fail after max retries', function () { - slouch.maxRetries = 3; - return fakeConflict().then(function () { return sporks.shouldThrow(function () { return slouch.doc.getModifyUpsert(utils.createdDB, '1', function (doc) {