Skip to content

Commit

Permalink
Use exponential backoff with jitter on 429 (#110)
Browse files Browse the repository at this point in the history
This can happen if there are too many clients trying to access the same base.
To accommodate this, use exponential backoff instead of a fixed backoff.
  • Loading branch information
mhahnenberg authored and Evan Hahn committed May 3, 2019
1 parent e027f08 commit 5b4ad19
Show file tree
Hide file tree
Showing 3 changed files with 15 additions and 5 deletions.
2 changes: 1 addition & 1 deletion lib/base.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ var Base = Class.extend({
},

runAction: function(method, path, queryParams, bodyData, callback) {
runAction(this, method, path, queryParams, bodyData, callback);
runAction(this, method, path, queryParams, bodyData, callback, 0);
},

_checkStatusForError: function(statusCode, body) {
Expand Down
3 changes: 2 additions & 1 deletion lib/internal_config.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
{
"RETRY_DELAY_IF_RATE_LIMITED": 5000
"INITIAL_RETRY_DELAY_IF_RATE_LIMITED": 5000,
"MAX_RETRY_DELAY_IF_RATE_LIMITED": 600000
}
15 changes: 12 additions & 3 deletions lib/run_action.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,15 @@ var objectToQueryParamString = require('./object_to_query_param_string');
// This will become require('xhr') in the browser.
var request = require('request');

function runAction(base, method, path, queryParams, bodyData, callback) {
// "Full Jitter" algorithm taken from https://aws.amazon.com/blogs/architecture/exponential-backoff-and-jitter/
function exponentialBackoffWithJitter(numberOfRetries, initialBackoffTimeMs, maxBackoffTimeMs) {
var rawBackoffTimeMs = initialBackoffTimeMs * Math.pow(2, numberOfRetries);
var clippedBackoffTimeMs = Math.min(maxBackoffTimeMs, rawBackoffTimeMs);
var jitteredBackoffTimeMs = Math.random() * clippedBackoffTimeMs;
return jitteredBackoffTimeMs;
}

function runAction(base, method, path, queryParams, bodyData, callback, numAttempts) {
var url = base._airtable._endpointUrl + '/v' + base._airtable._apiVersionMajor + '/' + base._id + path + '?' + objectToQueryParamString(queryParams);

var headers = {
Expand Down Expand Up @@ -49,9 +57,10 @@ function runAction(base, method, path, queryParams, bodyData, callback) {
}

if (resp.statusCode === 429 && !base._airtable._noRetryIfRateLimited) {
var backoffDelayMs = exponentialBackoffWithJitter(numAttempts, internalConfig.INITIAL_RETRY_DELAY_IF_RATE_LIMITED, internalConfig.MAX_RETRY_DELAY_IF_RATE_LIMITED);
setTimeout(function() {
runAction(base, method, path, queryParams, bodyData, callback);
}, internalConfig.RETRY_DELAY_IF_RATE_LIMITED);
runAction(base, method, path, queryParams, bodyData, callback, numAttempts + 1);
}, backoffDelayMs);
return;
}

Expand Down

0 comments on commit 5b4ad19

Please sign in to comment.