diff --git a/packages/retry-strategies/README.md b/packages/retry-strategies/README.md index 8e632a8..9ef1b70 100644 --- a/packages/retry-strategies/README.md +++ b/packages/retry-strategies/README.md @@ -128,7 +128,7 @@ Increases the delay exponentially using the AWS algorithm. ```typescript const strategy = exponential(100, 5000); -// base: 100, cap: 5000 (optional, default: Infinity) +// base: 100 (optional, default: 0), cap: 5000 (optional, default: Infinity) // Delays: 100ms, 200ms, 400ms, 800ms, 1600ms, 3200ms, 5000ms... ``` @@ -150,7 +150,7 @@ Increases the delay following the Fibonacci sequence. ```typescript const strategy = fibonacci(100, 10000); -// base: 100, cap: 10000 (default: Infinity) +// base: 100 (optional, default: 0), cap: 10000 (optional, default: Infinity) // Delays: 100ms, 100ms, 200ms, 300ms, 500ms, 800ms, 1300ms, 2100ms... ``` @@ -161,7 +161,7 @@ AWS FullJitter algorithm - adds randomness to exponential backoff. ```typescript const strategy = fullJitter(100, 5000); -// base: 100, cap: 5000 (default: Infinity) +// base: 100 (optional, default: 0), cap: 5000 (optional, default: Infinity) // Delays: random values between 0 and exponential cap ``` @@ -172,7 +172,7 @@ AWS EqualJitter algorithm - balances consistency and randomness. ```typescript const strategy = equalJitter(100, 5000); -// base: 100, cap: 5000 (default: Infinity) +// base: 100 (optional, default: 0), cap: 5000 (optional, default: Infinity) ``` ### `DecorrelatedJitterBackoff` / `decorrelatedJitter()` @@ -182,7 +182,7 @@ AWS DecorrelatedJitter algorithm - each delay based on previous delay. ```typescript const strategy = decorrelatedJitter(100, 10000); -// base: 100, cap: 10000 (default: Infinity) +// base: 100 (optional, default: 0), cap: 10000 (optional, default: Infinity) ``` ### `ConstantBackoff` / `constant()` diff --git a/packages/retry-strategies/src/backoff/decorrelated-jitter-backoff.test.ts b/packages/retry-strategies/src/backoff/decorrelated-jitter-backoff.test.ts index 451fd36..c10e972 100644 --- a/packages/retry-strategies/src/backoff/decorrelated-jitter-backoff.test.ts +++ b/packages/retry-strategies/src/backoff/decorrelated-jitter-backoff.test.ts @@ -41,6 +41,31 @@ suite("Decorrelated jitter backoff strategy (Unit)", () => { ); }); + test("uses default base when not provided", (ctx: TestContext) => { + ctx.plan(3); + + // Arrange + const backoff = new DecorrelatedJitterBackoff(); + + // Act & Assert + // With base = 0, random(0, 0) = 0 + ctx.assert.strictEqual( + backoff.nextBackoff(), + 0, + "should return 0ms on first call with default base", + ); + ctx.assert.strictEqual( + backoff.nextBackoff(), + 0, + "should return 0ms on second call with default base", + ); + ctx.assert.strictEqual( + backoff.nextBackoff(), + 0, + "should return 0ms on third call with default base", + ); + }); + test("returns delays based on previous delay", (ctx: TestContext) => { ctx.plan(5); diff --git a/packages/retry-strategies/src/backoff/decorrelated-jitter-backoff.ts b/packages/retry-strategies/src/backoff/decorrelated-jitter-backoff.ts index 0bcc0af..c09176b 100644 --- a/packages/retry-strategies/src/backoff/decorrelated-jitter-backoff.ts +++ b/packages/retry-strategies/src/backoff/decorrelated-jitter-backoff.ts @@ -17,11 +17,11 @@ export class DecorrelatedJitterBackoff implements BackoffStrategy { /** * Creates a new DecorrelatedJitterBackoff instance. * - * @param base - Base delay in milliseconds (>= 0) + * @param base - Base delay in milliseconds (>= 0, default: 0) * @param cap - Maximum delay in milliseconds (>= base, default: Infinity) * @throws {RangeError} If base or cap is invalid */ - public constructor(base: number, cap: number = Number.POSITIVE_INFINITY) { + public constructor(base: number = 0, cap: number = Number.POSITIVE_INFINITY) { if (Number.isNaN(base)) { throw new RangeError(`Base must not be NaN`); } @@ -73,7 +73,7 @@ export class DecorrelatedJitterBackoff implements BackoffStrategy { * Decorrelates retry attempts to avoid synchronization between clients. * Generally results in shorter overall wait times. * - * @param base - Base delay in milliseconds (>= 0) + * @param base - Base delay in milliseconds (>= 0, default: 0) * @param cap - Maximum delay in milliseconds (>= base, default: Infinity) * @returns DecorrelatedJitterBackoff instance * @throws {RangeError} If base or cap is invalid @@ -81,6 +81,6 @@ export class DecorrelatedJitterBackoff implements BackoffStrategy { * @see {@link https://aws.amazon.com/blogs/architecture/exponential-backoff-and-jitter/ AWS Exponential Backoff And Jitter} */ export const decorrelatedJitter = ( - base: number, + base: number = 0, cap: number = Number.POSITIVE_INFINITY, ): DecorrelatedJitterBackoff => new DecorrelatedJitterBackoff(base, cap); diff --git a/packages/retry-strategies/src/backoff/equal-jitter-backoff.test.ts b/packages/retry-strategies/src/backoff/equal-jitter-backoff.test.ts index 3f129ae..80a3935 100644 --- a/packages/retry-strategies/src/backoff/equal-jitter-backoff.test.ts +++ b/packages/retry-strategies/src/backoff/equal-jitter-backoff.test.ts @@ -38,6 +38,31 @@ suite("Equal jitter backoff strategy (Unit)", () => { ); }); + test("uses default base when not provided", (ctx: TestContext) => { + ctx.plan(3); + + // Arrange + const backoff = new EqualJitterBackoff(); + + // Act & Assert + // With base = 0, temp = 0, so (0/2) + random(0, 0/2) = 0 + ctx.assert.strictEqual( + backoff.nextBackoff(), + 0, + "should return 0ms on first call with default base", + ); + ctx.assert.strictEqual( + backoff.nextBackoff(), + 0, + "should return 0ms on second call with default base", + ); + ctx.assert.strictEqual( + backoff.nextBackoff(), + 0, + "should return 0ms on third call with default base", + ); + }); + test("returns delays that are half deterministic and half random", (ctx: TestContext) => { ctx.plan(5); diff --git a/packages/retry-strategies/src/backoff/equal-jitter-backoff.ts b/packages/retry-strategies/src/backoff/equal-jitter-backoff.ts index 91f9acb..4351a28 100644 --- a/packages/retry-strategies/src/backoff/equal-jitter-backoff.ts +++ b/packages/retry-strategies/src/backoff/equal-jitter-backoff.ts @@ -16,11 +16,11 @@ export class EqualJitterBackoff implements BackoffStrategy { /** * Creates a new EqualJitterBackoff instance. * - * @param base - Base delay in milliseconds (>= 0) + * @param base - Base delay in milliseconds (>= 0, default: 0) * @param cap - Maximum delay in milliseconds (>= base, default: Infinity) * @throws {RangeError} If base or cap is invalid */ - public constructor(base: number, cap: number = Number.POSITIVE_INFINITY) { + public constructor(base: number = 0, cap: number = Number.POSITIVE_INFINITY) { if (Number.isNaN(base)) { throw new RangeError(`Base must not be NaN`); } @@ -69,7 +69,7 @@ export class EqualJitterBackoff implements BackoffStrategy { * * Provides more predictable timing than FullJitter while still preventing thundering herd. * - * @param base - Base delay in milliseconds (>= 0) + * @param base - Base delay in milliseconds (>= 0, default: 0) * @param cap - Maximum delay in milliseconds (>= base, default: Infinity) * @returns EqualJitterBackoff instance * @throws {RangeError} If base or cap is invalid @@ -77,6 +77,6 @@ export class EqualJitterBackoff implements BackoffStrategy { * @see {@link https://aws.amazon.com/blogs/architecture/exponential-backoff-and-jitter/ AWS Exponential Backoff And Jitter} */ export const equalJitter = ( - base: number, + base: number = 0, cap: number = Number.POSITIVE_INFINITY, ): EqualJitterBackoff => new EqualJitterBackoff(base, cap); diff --git a/packages/retry-strategies/src/backoff/exponential-backoff.test.ts b/packages/retry-strategies/src/backoff/exponential-backoff.test.ts index c04d478..1b383a9 100644 --- a/packages/retry-strategies/src/backoff/exponential-backoff.test.ts +++ b/packages/retry-strategies/src/backoff/exponential-backoff.test.ts @@ -152,6 +152,30 @@ suite("Exponential backoff strategy (Unit)", () => { "should continue growing without artificial cap", ); // 100 * 2^3 = 800 }); + + test("uses default base when not provided", (ctx: TestContext) => { + ctx.plan(3); + + // Arrange + const backoff = new ExponentialBackoff(); + + // Act & Assert + ctx.assert.strictEqual( + backoff.nextBackoff(), + 0, + "should return 0ms on first call with default base", + ); // 0 * 2^0 = 0 + ctx.assert.strictEqual( + backoff.nextBackoff(), + 0, + "should return 0ms on second call with default base", + ); // 0 * 2^1 = 0 + ctx.assert.strictEqual( + backoff.nextBackoff(), + 0, + "should return 0ms on third call with default base", + ); // 0 * 2^2 = 0 + }); }); describe("strategy reset", () => { diff --git a/packages/retry-strategies/src/backoff/exponential-backoff.ts b/packages/retry-strategies/src/backoff/exponential-backoff.ts index 1c6a379..338f26f 100644 --- a/packages/retry-strategies/src/backoff/exponential-backoff.ts +++ b/packages/retry-strategies/src/backoff/exponential-backoff.ts @@ -15,11 +15,11 @@ export class ExponentialBackoff implements BackoffStrategy { /** * Creates a new ExponentialBackoff instance. * - * @param base - Base delay in milliseconds (>= 0) + * @param base - Base delay in milliseconds (>= 0, default: 0) * @param cap - Maximum delay in milliseconds (>= base, default: Infinity) * @throws {RangeError} If base or cap is invalid */ - public constructor(base: number, cap: number = Number.POSITIVE_INFINITY) { + public constructor(base: number = 0, cap: number = Number.POSITIVE_INFINITY) { if (Number.isNaN(base)) { throw new RangeError(`Base must not be NaN`); } @@ -64,7 +64,7 @@ export class ExponentialBackoff implements BackoffStrategy { * Increases the delay exponentially using the AWS algorithm. * Formula: `min(cap, base * 2^n)` * - * @param base - Base delay in milliseconds (>= 0) + * @param base - Base delay in milliseconds (>= 0, default: 0) * @param cap - Maximum delay in milliseconds (>= base, default: Infinity) * @returns ExponentialBackoff instance * @throws {RangeError} If base or cap is invalid @@ -72,6 +72,6 @@ export class ExponentialBackoff implements BackoffStrategy { * @see {@link https://aws.amazon.com/blogs/architecture/exponential-backoff-and-jitter/ AWS Exponential Backoff And Jitter} */ export const exponential = ( - base: number, + base: number = 0, cap: number = Number.POSITIVE_INFINITY, ): ExponentialBackoff => new ExponentialBackoff(base, cap); diff --git a/packages/retry-strategies/src/backoff/fibonacci-backoff.test.ts b/packages/retry-strategies/src/backoff/fibonacci-backoff.test.ts index cf0071f..4b049b2 100644 --- a/packages/retry-strategies/src/backoff/fibonacci-backoff.test.ts +++ b/packages/retry-strategies/src/backoff/fibonacci-backoff.test.ts @@ -197,6 +197,30 @@ suite("Fibonacci backoff strategy (Unit)", () => { "should continue growing without artificial cap", ); // 500 }); + + test("uses default base when not provided", (ctx: TestContext) => { + ctx.plan(3); + + // Arrange + const backoff = new FibonacciBackoff(); + + // Act & Assert + ctx.assert.strictEqual( + backoff.nextBackoff(), + 0, + "should return 0ms on first call with default base", + ); // base = 0 + ctx.assert.strictEqual( + backoff.nextBackoff(), + 0, + "should return 0ms on second call with default base", + ); // 0 + 0 = 0 + ctx.assert.strictEqual( + backoff.nextBackoff(), + 0, + "should return 0ms on third call with default base", + ); // 0 + 0 = 0 + }); }); describe("strategy reset", () => { diff --git a/packages/retry-strategies/src/backoff/fibonacci-backoff.ts b/packages/retry-strategies/src/backoff/fibonacci-backoff.ts index c9552e5..e9d2643 100644 --- a/packages/retry-strategies/src/backoff/fibonacci-backoff.ts +++ b/packages/retry-strategies/src/backoff/fibonacci-backoff.ts @@ -14,11 +14,11 @@ export class FibonacciBackoff implements BackoffStrategy { /** * Creates a new FibonacciBackoff instance. * - * @param base - Base delay in milliseconds (>= 0) + * @param base - Base delay in milliseconds (>= 0, default: 0) * @param cap - Maximum delay in milliseconds (>= base, default: Infinity) * @throws {RangeError} If base or cap is invalid */ - public constructor(base: number, cap: number = Number.POSITIVE_INFINITY) { + public constructor(base: number = 0, cap: number = Number.POSITIVE_INFINITY) { if (Number.isNaN(base)) { throw new RangeError(`Base must not be NaN`); } @@ -70,12 +70,12 @@ export class FibonacciBackoff implements BackoffStrategy { * Increases the delay following the Fibonacci sequence. * Formula: `min(cap, base * fib(n))` * - * @param base - Base delay in milliseconds (>= 0) + * @param base - Base delay in milliseconds (>= 0, default: 0) * @param cap - Maximum delay in milliseconds (>= base, default: Infinity) * @returns FibonacciBackoff instance * @throws {RangeError} If base or cap is invalid */ export const fibonacci = ( - base: number, + base: number = 0, cap: number = Number.POSITIVE_INFINITY, ): FibonacciBackoff => new FibonacciBackoff(base, cap); diff --git a/packages/retry-strategies/src/backoff/full-jitter-backoff.test.ts b/packages/retry-strategies/src/backoff/full-jitter-backoff.test.ts index 9d6627b..8105d34 100644 --- a/packages/retry-strategies/src/backoff/full-jitter-backoff.test.ts +++ b/packages/retry-strategies/src/backoff/full-jitter-backoff.test.ts @@ -38,6 +38,31 @@ suite("Full jitter backoff strategy (Unit)", () => { ); }); + test("uses default base when not provided", (ctx: TestContext) => { + ctx.plan(3); + + // Arrange + const backoff = new FullJitterBackoff(); + + // Act & Assert + // With base = 0, max delay is 0, so random(0, 0) = 0 + ctx.assert.strictEqual( + backoff.nextBackoff(), + 0, + "should return 0ms on first call with default base", + ); + ctx.assert.strictEqual( + backoff.nextBackoff(), + 0, + "should return 0ms on second call with default base", + ); + ctx.assert.strictEqual( + backoff.nextBackoff(), + 0, + "should return 0ms on third call with default base", + ); + }); + test("returns random delays within exponential bounds", (ctx: TestContext) => { ctx.plan(5); diff --git a/packages/retry-strategies/src/backoff/full-jitter-backoff.ts b/packages/retry-strategies/src/backoff/full-jitter-backoff.ts index 4b76751..ec152b0 100644 --- a/packages/retry-strategies/src/backoff/full-jitter-backoff.ts +++ b/packages/retry-strategies/src/backoff/full-jitter-backoff.ts @@ -16,11 +16,11 @@ export class FullJitterBackoff implements BackoffStrategy { /** * Creates a new FullJitterBackoff instance. * - * @param base - Base delay in milliseconds (>= 0) + * @param base - Base delay in milliseconds (>= 0, default: 0) * @param cap - Maximum delay in milliseconds (>= base, default: Infinity) * @throws {RangeError} If base or cap is invalid */ - public constructor(base: number, cap: number = Number.POSITIVE_INFINITY) { + public constructor(base: number = 0, cap: number = Number.POSITIVE_INFINITY) { if (Number.isNaN(base)) { throw new RangeError(`Base must not be NaN`); } @@ -68,7 +68,7 @@ export class FullJitterBackoff implements BackoffStrategy { * * Prevents thundering herd problems where multiple clients retry simultaneously. * - * @param base - Base delay in milliseconds (>= 0) + * @param base - Base delay in milliseconds (>= 0, default: 0) * @param cap - Maximum delay in milliseconds (>= base, default: Infinity) * @returns FullJitterBackoff instance * @throws {RangeError} If base or cap is invalid @@ -76,6 +76,6 @@ export class FullJitterBackoff implements BackoffStrategy { * @see {@link https://aws.amazon.com/blogs/architecture/exponential-backoff-and-jitter/ AWS Exponential Backoff And Jitter} */ export const fullJitter = ( - base: number, + base: number = 0, cap: number = Number.POSITIVE_INFINITY, ): FullJitterBackoff => new FullJitterBackoff(base, cap);