Files
factorio-clusterio/packages/lib/src/ExponentialBackoff.ts
T
Hornwitser c53781d16c Add time unit suffixes to variables (#561)
Add suffixes like Ms and Ns to variables whos time unit is not in
seconds.  This is aligning with a new code style of putting units
on variables that are not in SI base units.
2024-01-20 13:26:55 +01:00

80 lines
2.6 KiB
TypeScript

/**
* Helper class for implementing the exponential backoff algorithm
*
* Provides a convenient interface for implementing an exponential backoff
* when reconnecting or resending in a distributed network.
*
* Once instantiated the delay method will return the next interval to wait
* for in milliseconds before making another attempt. For each attempt the
* delay chosen is a random value between 0 and the current maximum delay
* wwhich starts at a value of 2 * `base` and doubles for ever attempt up to
* the passed value of `max`. The value for the delay will reset back to
* `base` if more time than the value for `reset` has passed between two
* invocations of delay.
*/
export default class ExponentialBackoff {
/**
* Base delay in seconds. The first delay will on average be this
* long, but can be anywhere between 0 and 2 times this value.
*/
base = 1;
/**
* Maximum delay in seconds. A delay will not take more than this
* value.
*/
max = 60;
/**
* Time in seconds between two invocations of delay which will cause
* it to start over with `base` instead of doubling.
*/
reset: number;
private _exp = 0;
private _lastInvocationTimeMs = Date.now();
/**
* Construct ExponentialBackoff helper
*
* The options passed may also be modified on the instance at runtime.
*
* @param options -
* Options for this exponential backoff helper
* @param [options.base=1] -
* Base delay in seconds. The first delay will on average be this
* long, but can be anywhere between 0 and 2 times this value.
* @param [options.max=60] -
* Maximum delay in seconds. A delay will not take more than this
* value.
* @param [options.reset=2*max] -
* Time in seconds between two invocations of delay which will cause
* it to start over with `base` instead of doubling.
*/
constructor(options: { base?: number, max?: number, reset?: number } = {}) {
this.base = options.base || 1;
this.max = options.max || 60;
this.reset = options.reset || 2 * this.max;
}
/**
* Compute the next delay to wait
*
* Returns the next delay to wait according to the exponential backoff
* algorithm. This is a value between 0 and min(2 * options.base *
* 2<sup>retries</sup>, optinos.max).
*
* @returns time in milliseconds to wait.
*/
delay() {
let invocationTimeMs = Date.now();
let interval = (invocationTimeMs - this._lastInvocationTimeMs) / 1000;
this._lastInvocationTimeMs = invocationTimeMs;
if (interval > this.reset) {
this._exp = 0;
}
this._exp = Math.min(this._exp + 1, Math.log2(this.max));
return Math.random() * this.base * 2 ** this._exp * 1000;
}
}