aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRagnis Armus <ragnis@armus.ee>2019-12-08 21:48:50 +0200
committerRagnis Armus <ragnis@armus.ee>2019-12-11 23:03:18 +0200
commit8dad185a495e532ac19c6df3d64c6980f6d1325c (patch)
tree6b07d2fc5acb36713fe0fa79a74b5c7098295c08
parent71236ff3cf5f5450ccbe4a1296d26b826a447fc0 (diff)
Implement incremental back-off
-rw-r--r--lib/agent.js28
-rw-r--r--lib/agent.test.js23
2 files changed, 42 insertions, 9 deletions
diff --git a/lib/agent.js b/lib/agent.js
index 34ea66e..347bbcd 100644
--- a/lib/agent.js
+++ b/lib/agent.js
@@ -5,6 +5,7 @@ const kOptions = Symbol('options');
const kTargets = Symbol('targets');
const defaultTtl = 5;
+const defaultMaxRetryWait = 30;
const createError = (code, msg) => Object.assign(new Error(msg), { code });
@@ -45,15 +46,18 @@ class Target {
this.logger = options.logger;
this.maxStale = options.maxStale;
+ this.maxRetryWait = options.maxRetryWait || defaultMaxRetryWait;
this.hostname = hostname;
this.getTime = now;
this.ttl = defaultTtl;
- this.err = undefined;
this.addrs = undefined;
this.i = 0;
+ this.err = undefined;
+ this.errSeq = 0;
+
this.renewPromise = undefined;
/**
@@ -118,30 +122,35 @@ class Target {
this.ttl = newTtl;
this.addrs = newAddrs;
this.err = undefined;
+ this.errSeq = 0;
if (this.maxStale !== undefined) {
this.maxStaleAt = this.getTime() + this.ttl + this.maxStale;
}
+
+ this.renewNextAfter = this.getTime() + this.ttl;
},
(err) => {
- if (err.code === dns.CANCELLED && this.addrs !== undefined) {
- // Ignore the error, keep using cached data.
- return;
- }
this.err = err;
+ this.errSeq++;
+
+ this.renewNextAfter = this.getTime() + this.getRetryWait(this.errSeq);
},
).then(() => {
this.renewPromise = undefined;
- this.renewNextAfter = this.getTime() + this.ttl;
-
- // TODO: Use a different interval for re-trying in case of
- // errors.
});
}
return this.renewPromise;
}
+ /**
+ * Get the number of seconds to wait before re-trying after an error.
+ */
+ getRetryWait(seq) {
+ return Math.min(Math.pow(2, seq) + Math.random() * 2, this.maxRetryWait);
+ }
+
createError(err) {
const retry = (this.renewNextAfter - this.getTime()).toFixed(3);
const msg = `${err.message}; re-trying after ${retry} seconds`;
@@ -157,6 +166,7 @@ class Agent {
* @param {Object} [options]
* @param {Object} [options.logger]
* @param {number} [options.maxStale]
+ * @param {number} [options.maxRetryWait]
* @param {string} [options.nameservers]
*/
constructor(options = {}) {
diff --git a/lib/agent.test.js b/lib/agent.test.js
index 3b35589..147d0df 100644
--- a/lib/agent.test.js
+++ b/lib/agent.test.js
@@ -37,6 +37,7 @@ describe('Target', function () {
const target = new Target({});
target.getTime = () => time;
+ target.getRetryWait = () => 10;
target.resolver = async () => {
calls++;
return [{ name: 'foo.com', port: 80, ttl: 20 }];
@@ -89,6 +90,7 @@ describe('Target', function () {
const target = new Target({ maxStale: 10 });
target.getTime = () => time;
+ target.getRetryWait = () => 10;
target.resolver = async () => [
{ name: 'foo.com', port: 80, ttl: 10 },
];
@@ -126,6 +128,7 @@ describe('Target', function () {
const target = new Target({});
target.getTime = () => time;
+ target.getRetryWait = () => 10;
target.resolver = async () => [
{ name: 'foo.com', port: 80, ttl: 10 },
];
@@ -160,6 +163,7 @@ describe('Target', function () {
const target = new Target({ maxStale: 0 });
target.getTime = () => time;
+ target.getRetryWait = () => 10;
target.resolver = async () => [
{ name: 'foo.com', port: 80, ttl: 10 },
];
@@ -184,5 +188,24 @@ describe('Target', function () {
},
);
});
+
+ it('should incrementally increase retry time on error', async function () {
+ const target = new Target({ maxRetryWait: 31 });
+
+ let wait = target.getRetryWait(1);
+ assert(wait >= 2 && wait <= 4);
+
+ wait = target.getRetryWait(2);
+ assert(wait >= 4 && wait <= 6);
+
+ wait = target.getRetryWait(3);
+ assert(wait >= 8 && wait <= 10);
+
+ wait = target.getRetryWait(4);
+ assert(wait >= 16 && wait <= 18);
+
+ wait = target.getRetryWait(5);
+ assert(wait >= 30 && wait <= 31);
+ });
});
});