From 21749aaa670c6a9ea343f7f9846f6fbc0640e3e5 Mon Sep 17 00:00:00 2001 From: Justus Perlwitz Date: Fri, 19 Apr 2024 17:52:44 +0900 Subject: [PATCH] Add calculateRetryDelayFactor helper function This is used to calculate the exponentially increasing delay. The function is used to calculate the reconnection delay - but does not keep track of the current reconnection attempt. Keeping track of the current reconnection attempt is reserved for the next few commits. --- __tests__/index/retryConnectionDelay.test.ts | 29 +++++++++++++++ src/index.ts | 37 ++++++++++++++++++-- 2 files changed, 63 insertions(+), 3 deletions(-) diff --git a/__tests__/index/retryConnectionDelay.test.ts b/__tests__/index/retryConnectionDelay.test.ts index a0c4376..c483236 100644 --- a/__tests__/index/retryConnectionDelay.test.ts +++ b/__tests__/index/retryConnectionDelay.test.ts @@ -1,6 +1,8 @@ // File Dependencies import Sarus from "../../src/index"; import { WS } from "jest-websocket-mock"; +import { calculateRetryDelayFactor } from "../../src/index"; +import type { ExponentialBackoffParams } from "../../src/index"; const url = "ws://localhost:1234"; @@ -61,3 +63,30 @@ describe("retry connection delay", () => { }); }); }); + +describe("Exponential backoff delay", () => { + it("will never be more than 8000 ms with rate set to 2", () => { + // The initial delay shall be 1 s + const initialDelay = 1000; + const exponentialBackoffParams: ExponentialBackoffParams = { + backoffRate: 2, + // We put the ceiling at exactly 8000 ms + backoffLimit: 8000, + }; + expect( + calculateRetryDelayFactor(exponentialBackoffParams, initialDelay, 0), + ).toBe(1000); + expect( + calculateRetryDelayFactor(exponentialBackoffParams, initialDelay, 1), + ).toBe(2000); + expect( + calculateRetryDelayFactor(exponentialBackoffParams, initialDelay, 2), + ).toBe(4000); + expect( + calculateRetryDelayFactor(exponentialBackoffParams, initialDelay, 3), + ).toBe(8000); + expect( + calculateRetryDelayFactor(exponentialBackoffParams, initialDelay, 4), + ).toBe(8000); + }); +}); diff --git a/src/index.ts b/src/index.ts index 153b3e8..39bc0aa 100644 --- a/src/index.ts +++ b/src/index.ts @@ -78,6 +78,29 @@ export interface ExponentialBackoffParams { backoffLimit: number; } +/* + * Calculate the exponential backoff delay for a given number of connection + * attempts. + * @param {ExponentialBackoffParams} params - configuration parameters for + * exponential backoff. + * @param {number} initialDelay - the initial delay before any backoff is + * applied + * @param {number} connectionAttempts - the number of connection attempts + * that have previously failed, excluding the original connection attempt that + * succeeded + * @returns {void} - set does not return + */ +export function calculateRetryDelayFactor( + params: ExponentialBackoffParams, + initialDelay: number, + connectionAttempts: number, +): number { + return Math.min( + initialDelay * Math.pow(params.backoffRate, connectionAttempts), + params.backoffLimit, + ); +} + export interface SarusClassParams { url: string; binaryType?: BinaryType; @@ -363,12 +386,20 @@ export default class Sarus { } /** - * Reconnects the WebSocket client based on the retryConnectionDelay setting. + * Reconnects the WebSocket client based on the retryConnectionDelay and + * ExponentialBackoffParam setting. */ reconnect() { const self = this; - const { retryConnectionDelay } = self; - setTimeout(self.connect, retryConnectionDelay); + const { retryConnectionDelay, exponentialBackoff } = self; + + // If no exponential backoff is enabled, retryConnectionDelay will + // be scaled by a factor of 1 and it will stay the original value. + const delay = exponentialBackoff + ? calculateRetryDelayFactor(exponentialBackoff, retryConnectionDelay, 0) + : retryConnectionDelay; + + setTimeout(self.connect, delay); } /**