Skip to content

Commit

Permalink
Merge pull request #2 from cpv123/allow-varying-delays
Browse files Browse the repository at this point in the history
Allow varying delays between retries
  • Loading branch information
luispabon authored Feb 7, 2019
2 parents f863b26 + a067ca5 commit d4da449
Show file tree
Hide file tree
Showing 3 changed files with 264 additions and 5 deletions.
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,16 @@ superagent
.retry(2, 5000, [401, 404]) // retry twice before responding, wait 5 seconds between failures, do not retry when response is success, or 401 or 404
.end(onresponse);

superagent
.get('https://segment.io')
.retry(3, [1000, 3000, 10000], [401, 404]) // retry three times before responding, first wait 1 second, then 3 seconds, and finally 10 seconds between failures, do not retry when response is success, or 401 or 404
.end(onresponse);

superagent
.get('https://segment.io')
.retry(5, [1000, 3000], [401, 404]) // retry five times before responding, first wait 1 second, and then wait 3 seconds between all other failures, do not retry when response is success, or 401 or 404
.end(onresponse);

function onresponse (err, res) {
console.log(res.status, res.headers);
console.log(res.body);
Expand Down
35 changes: 30 additions & 5 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,10 +69,17 @@ function shouldRetry (err, res, allowedStatuses) {
*/
function callback (err, res) {
if (this._maxRetries && this._retries++ < this._maxRetries && shouldRetry(err, res, this._allowedStatuses)) {
var delay
if (!this._retries) {
delay = 0
} else {
delay = this._retryDelays[this._retries - 1]
}

var req = this
return setTimeout(function () {
return req._retry()
}, this._retryDelay)
}, delay)
}

var fn = this._callback
Expand All @@ -87,16 +94,16 @@ function callback (err, res) {
}

/**
* Override Request retry to also set a delay.
* Override Request retry to also set delays between requests.
*
* In miliseconds.
*
* @param {Number} retries
* @param {Number} delay
* @param {Number[] || Number} delays
* @param {Number[]} allowedStatuses
* @return {retry}
*/
function retry (retries, delay, allowedStatuses) {
function retry (retries, delays, allowedStatuses) {
if (arguments.length === 0 || retries === true) {
retries = 1
}
Expand All @@ -105,9 +112,27 @@ function retry (retries, delay, allowedStatuses) {
retries = 0
}

if (typeof delays === 'number') {
delays = [delays]
}

var numberOfDelays = delays.length
var diff = retries - numberOfDelays
if (diff !== 0) {
if (diff < 0) {
throw new Error('Cannot have more delays than retries')
} else {
// Extrapolate delays list until there is a delay for each retry
var finalDelay = delays[numberOfDelays - 1]
for (var i = 0; i < (diff + 1); i++) {
delays.push(finalDelay)
}
}
}

this._maxRetries = retries
this._retries = 0
this._retryDelay = delay || 0
this._retryDelays = delays || [0]
this._allowedStatuses = allowedStatuses || []

return this
Expand Down
224 changes: 224 additions & 0 deletions tests/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,28 @@ describe('superagent-retry-delay', function () {
server = app.listen(port, done)
})

afterEach(function () {
requests = 0
})

it('should not retry on success', function (done) {
agent
.get('http://localhost:' + port)
.retry(5, 17)
.end(function (err, res) {
res.text.should.eql('hello!')
requests.should.eql(1)
done(err)
})
})

it('should not retry on success - multiple delays format', function (done) {
agent
.get('http://localhost:' + port)
.retry(5, [17, 17, 17, 17, 17])
.end(function (err, res) {
res.text.should.eql('hello!')
requests.should.eql(1)
done(err)
})
})
Expand Down Expand Up @@ -62,6 +76,10 @@ describe('superagent-retry-delay', function () {
server = app.listen(port, done)
})

afterEach(function () {
requests = 0
})

it('should not retry on handled errors', function (done) {
agent
.get('http://localhost:' + port)
Expand All @@ -73,6 +91,17 @@ describe('superagent-retry-delay', function () {
})
})

it('should not retry on handled errors - multiple delays format', function (done) {
agent
.get('http://localhost:' + port)
.retry(5, [13, 13, 13, 13, 13], [404])
.end(function (err, res) {
res.status.should.eql(404)
requests.should.eql(3)
done(err)
})
})

after(function (done) { server.close(done) })
})

Expand All @@ -95,6 +124,10 @@ describe('superagent-retry-delay', function () {
server = app.listen(port, done)
})

afterEach(function () {
requests = 0
})

it('should retry on errors', function (done) {
agent
.get('http://localhost:' + port)
Expand All @@ -114,6 +147,25 @@ describe('superagent-retry-delay', function () {
})
})

it('should retry on errors - multiple delays format', function (done) {
agent
.get('http://localhost:' + port)
.end(function (err, res) {
res.status.should.eql(503)

// appease eslint, do nothing with error to allow it to bubble up
if (err) { }
})

agent
.get('http://localhost:' + port)
.retry(5, [17, 17, 17, 17, 17])
.end(function (err, res) {
res.text.should.eql('hello!')
done(err)
})
})

after(function (done) { server.close(done) })
})

Expand All @@ -136,6 +188,10 @@ describe('superagent-retry-delay', function () {
server = app.listen(port, done)
})

afterEach(function () {
requests = 0
})

it('should retry on errors', function (done) {
agent
.get('http://localhost:' + port)
Expand All @@ -156,6 +212,26 @@ describe('superagent-retry-delay', function () {
})
})

it('should retry on errors - multiple delays format', function (done) {
agent
.get('http://localhost:' + port)
.end(function (err, res) {
res.status.should.eql(500)

// appease eslint, do nothing with error to allow it to bubble up
if (err) { }
})

agent
.get('http://localhost:' + port)
.retry(5, [13, 13, 13, 13, 13])
.end(function (err, res) {
res.text.should.eql('hello!')
requests.should.eql(5)
done(err)
})
})

after(function (done) { server.close(done) })
})

Expand All @@ -178,6 +254,10 @@ describe('superagent-retry-delay', function () {
server = app.listen(port, done)
})

afterEach(function () {
requests = 0
})

it('should retry on errors', function (done) {
agent
.get('http://localhost:' + port)
Expand All @@ -198,6 +278,26 @@ describe('superagent-retry-delay', function () {
})
})

it('should retry on errors - multiple delays format', function (done) {
agent
.get('http://localhost:' + port)
.end(function (err, res) {
res.status.should.eql(404)

// appease eslint, do nothing with error to allow it to bubble up
if (err) { }
})

agent
.get('http://localhost:' + port)
.retry(5, [13, 13, 13, 13, 13])
.end(function (err, res) {
res.text.should.eql('hello!')
requests.should.eql(5)
done(err)
})
})

after(function (done) { server.close(done) })
})

Expand All @@ -220,6 +320,10 @@ describe('superagent-retry-delay', function () {
server = app.listen(port, done)
})

afterEach(function () {
requests = 0
})

it('should retry on errors', function (done) {
agent
.get('http://localhost:' + port)
Expand All @@ -240,6 +344,126 @@ describe('superagent-retry-delay', function () {
})
})

it('should retry on errors - multiple delays format', function (done) {
agent
.get('http://localhost:' + port)
.end(function (err, res) {
res.status.should.eql(401)

// appease eslint, do nothing with error to allow it to bubble up
if (err) { }
})

agent
.get('http://localhost:' + port)
.retry(5, [13, 13, 13, 13, 13])
.end(function (err, res) {
res.text.should.eql('hello!')
requests.should.eql(5)
done(err)
})
})

after(function (done) { server.close(done) })
})

describe('specifying different delays between retries', function () {
let requests = 0
let delays
let delaysMeasured = []
let delaysAdjusted = []
let start
let now
const port = 10410
const app = express()
let server

before(function (done) {
app.get('/', function (req, res, next) {
requests++
if (requests === 1) {
start = new Date().valueOf()
} else {
now = new Date().valueOf()
delaysMeasured.push(now - start)
}
if (requests > 5) {
res.send('hello!')
} else {
res.sendStatus(401)
}
})

server = app.listen(port, done)
})

afterEach(function () {
requests = 0
delaysMeasured = []
delaysAdjusted = []
})

it('retries using the specified delays', function (done) {
delays = [100, 200, 300, 400, 500]

agent
.get('http://localhost:' + port)
.retry(5, delays)
.end(function (err, res) {
res.text.should.eql('hello!')
requests.should.eql(6)
delaysMeasured.length.should.equal(5)

// Create a list of the actual delays measured between consecutive retries
for (let i = 0; i < delaysMeasured.length; i++) {
if (i === 0) {
delaysAdjusted[i] = delaysMeasured[i]
} else {
delaysAdjusted[i] = delaysMeasured[i] - delaysMeasured[i - 1]
}
}

// Assert that each delay measured is close to the specified delay
for (let i = 0; i < delaysAdjusted.length; i++) {
let delayDiff = delaysAdjusted[i] - delays[i]
delayDiff.should.be.within(0, 20)
}

done(err)
})
})

it('extrapolates the list of delays', function (done) {
delays = [100, 200, 300]
const expectedDelays = [100, 200, 300, 300, 300]

agent
.get('http://localhost:' + port)
.retry(5, delays)
.end(function (err, res) {
res.text.should.eql('hello!')
requests.should.eql(6)
delaysMeasured.length.should.equal(5)

// Create a list of the actual delays measured between consecutive retries
for (let i = 0; i < delaysMeasured.length; i++) {
if (i === 0) {
delaysAdjusted[i] = delaysMeasured[i]
} else {
delaysAdjusted[i] = delaysMeasured[i] - delaysMeasured[i - 1]
}
}

// Assert that each delay measured is close to the specified delay
for (let i = 0; i < delaysAdjusted.length; i++) {
let delayDiff = delaysAdjusted[i] - expectedDelays[i]
delayDiff.should.be.within(0, 20)
}

done(err)
})
})

after(function (done) { server.close(done) })
})
})

0 comments on commit d4da449

Please sign in to comment.