Skip to content

Commit 73d1c78

Browse files
authoredMar 22, 2017
Adds jitter to DynamoDB retries and allows custom retry logic (#1418)
* Adds jitter to DynamoDB retries and allows custom retry logic * Updates retry tests
1 parent eea53f5 commit 73d1c78

File tree

7 files changed

+58
-11
lines changed

7 files changed

+58
-11
lines changed
 
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"type": "feature",
3+
"category": "DynamoDB",
4+
"description": "Adds ability to customize retry delays for DynamoDB. This previously worked for all services except DynamoDB. Also adds jitter to DynamoDB retries. See `AWS.Config.retryDelayOptions` for more information."
5+
}

‎lib/config.js

+5-6
Original file line numberDiff line numberDiff line change
@@ -96,12 +96,12 @@ var PromisesDependency;
9696
* AWS.config.update({retryDelayOptions: {customBackoff: function(retryCount) {
9797
* // returns delay in ms
9898
* }}});
99-
* @note This works with all services except DynamoDB.
10099
* @return [map] A set of options to configure the retry delay on retryable errors.
101100
* Currently supported options are:
102101
*
103102
* * **base** [Integer] — The base number of milliseconds to use in the
104-
* exponential backoff for operation retries. Defaults to 100 ms.
103+
* exponential backoff for operation retries. Defaults to 100 ms for all services accept
104+
* DynamoDB, where it defaults to 50ms.
105105
* * **customBackoff ** [function] — A custom function that accepts a retry count
106106
* and returns the amount of time to delay in milliseconds. The `base` option will be
107107
* ignored if this option is supplied.
@@ -216,7 +216,8 @@ AWS.Config = AWS.util.inherit({
216216
* the retry delay on retryable errors. Currently supported options are:
217217
*
218218
* * **base** [Integer] — The base number of milliseconds to use in the
219-
* exponential backoff for operation retries. Defaults to 100 ms.
219+
* exponential backoff for operation retries. Defaults to 100 ms for all
220+
* services accept DynamoDB, where it defaults to 50ms.
220221
* * **customBackoff ** [function] — A custom function that accepts a retry count
221222
* and returns the amount of time to delay in milliseconds. The `base` option will be
222223
* ignored if this option is supplied.
@@ -477,9 +478,7 @@ AWS.Config = AWS.util.inherit({
477478
systemClockOffset: 0,
478479
signatureVersion: null,
479480
signatureCache: true,
480-
retryDelayOptions: {
481-
base: 100
482-
},
481+
retryDelayOptions: {},
483482
useAccelerateEndpoint: false
484483
},
485484

‎lib/services/dynamodb.js

+6-1
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,12 @@ AWS.util.update(AWS.DynamoDB.prototype, {
4747
* @api private
4848
*/
4949
retryDelays: function retryDelays(retryCount) {
50-
var delay = retryCount > 0 ? (50 * Math.pow(2, retryCount - 1)) : 0;
50+
var retryDelayOptions = AWS.util.copy(this.config.retryDelayOptions);
51+
52+
if (typeof retryDelayOptions.base !== 'number') {
53+
retryDelayOptions.base = 50; // default for dynamodb
54+
}
55+
var delay = AWS.util.calculateRetryDelay(retryCount, retryDelayOptions);
5156
return delay;
5257
}
5358
});

‎lib/util.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -824,7 +824,7 @@ var util = {
824824
if (typeof customBackoff === 'function') {
825825
return customBackoff(retryCount);
826826
}
827-
var base = retryDelayOptions.base || 100;
827+
var base = typeof retryDelayOptions.base === 'number' ? retryDelayOptions.base : 100;
828828
var delay = Math.random() * (Math.pow(2, retryCount) * base);
829829
return delay;
830830
},

‎test/config.spec.coffee

-2
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,6 @@ describe 'AWS.Config', ->
6060
expect(configure(maxRetries: 2).maxRetries).to.equal(2)
6161

6262
describe 'retryDelayOptions', ->
63-
it 'defaults to "base: 100"', ->
64-
expect(configure().retryDelayOptions).to.eql({base: 100})
6563
it 'can set "base" to an integer', ->
6664
expect(configure(retryDelayOptions: {base: 30}).retryDelayOptions).to.eql({base: 30})
6765

‎test/service.spec.coffee

+24
Original file line numberDiff line numberDiff line change
@@ -337,6 +337,30 @@ describe 'AWS.Service', ->
337337
service.config.maxRetries = undefined
338338
expect(service.numRetries()).to.equal(13)
339339

340+
describe 'retryDelays', ->
341+
beforeEach ->
342+
helpers.spyOn(Math, 'random').andReturn 1
343+
344+
it 'has a default delay base of 100 ms', ->
345+
client = new AWS.Service({})
346+
expectedDelays = [100, 200, 400 ]
347+
actualDelays = (client.retryDelays(i) for i in [0..client.numRetries()-1])
348+
expect(actualDelays).to.eql(expectedDelays)
349+
350+
it 'can accept a user-defined delay base', ->
351+
client = new AWS.Service({retryDelayOptions: {base: 200}})
352+
expectedDelays = [ 200, 400, 800 ]
353+
actualDelays = (client.retryDelays(i) for i in [0..client.numRetries()-1])
354+
expect(actualDelays).to.eql(expectedDelays)
355+
356+
it 'can accept a user-defined custom backoff', ->
357+
customBackoff = (retryCount) ->
358+
return 100 * retryCount
359+
client = new AWS.Service({retryDelayOptions: {customBackoff: customBackoff}})
360+
expectedDelays = [ 0, 100, 200 ]
361+
actualDelays = (client.retryDelays(i) for i in [0..client.numRetries()-1])
362+
expect(actualDelays).to.eql(expectedDelays)
363+
340364
describe 'defineMethods', ->
341365
operations = null
342366
serviceConstructor = null

‎test/services/dynamodb.spec.coffee

+17-1
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,28 @@ describe 'AWS.DynamoDB', ->
2727
expect(ddb({ maxRetries: 2 }).numRetries()).to.equal(2)
2828

2929
describe 'retryDelays', ->
30+
beforeEach ->
31+
helpers.spyOn(Math, 'random').andReturn 1
3032

3133
it 'has a custom backoff function', ->
32-
expectedDelays = [ 0, 50, 100, 200, 400, 800, 1600, 3200, 6400, 12800 ]
34+
expectedDelays = [ 50, 100, 200, 400, 800, 1600, 3200, 6400, 12800, 25600 ]
3335
actualDelays = (client.retryDelays(i) for i in [0..client.numRetries()-1])
3436
expect(actualDelays).to.eql(expectedDelays)
3537

38+
it 'can accept a user-defined delay base', ->
39+
service = ddb({retryDelayOptions: {base: 100}})
40+
expectedDelays = [ 100, 200, 400, 800, 1600, 3200, 6400, 12800, 25600, 51200 ]
41+
actualDelays = (service.retryDelays(i) for i in [0..service.numRetries()-1])
42+
expect(actualDelays).to.eql(expectedDelays)
43+
44+
it 'can accept a user-defined custom backoff', ->
45+
customBackoff = (retryCount) ->
46+
return 100 * retryCount
47+
service = ddb({retryDelayOptions: {customBackoff: customBackoff}})
48+
expectedDelays = [ 0, 100, 200, 300, 400, 500, 600, 700, 800, 900 ]
49+
actualDelays = (service.retryDelays(i) for i in [0..service.numRetries()-1])
50+
expect(actualDelays).to.eql(expectedDelays)
51+
3652
describe 'CRC32 check', ->
3753
dynamo = null
3854

0 commit comments

Comments
 (0)
Please sign in to comment.