Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions packages/retry-strategies/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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...
```

Expand All @@ -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...
```

Expand All @@ -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
```

Expand All @@ -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()`
Expand All @@ -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()`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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`);
}
Expand Down Expand Up @@ -73,14 +73,14 @@ 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
*
* @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);
25 changes: 25 additions & 0 deletions packages/retry-strategies/src/backoff/equal-jitter-backoff.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
8 changes: 4 additions & 4 deletions packages/retry-strategies/src/backoff/equal-jitter-backoff.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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`);
}
Expand Down Expand Up @@ -69,14 +69,14 @@ 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
*
* @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);
24 changes: 24 additions & 0 deletions packages/retry-strategies/src/backoff/exponential-backoff.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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", () => {
Expand Down
8 changes: 4 additions & 4 deletions packages/retry-strategies/src/backoff/exponential-backoff.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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`);
}
Expand Down Expand Up @@ -64,14 +64,14 @@ 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
*
* @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);
24 changes: 24 additions & 0 deletions packages/retry-strategies/src/backoff/fibonacci-backoff.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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", () => {
Expand Down
8 changes: 4 additions & 4 deletions packages/retry-strategies/src/backoff/fibonacci-backoff.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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`);
}
Expand Down Expand Up @@ -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);
25 changes: 25 additions & 0 deletions packages/retry-strategies/src/backoff/full-jitter-backoff.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
8 changes: 4 additions & 4 deletions packages/retry-strategies/src/backoff/full-jitter-backoff.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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`);
}
Expand Down Expand Up @@ -68,14 +68,14 @@ 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
*
* @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);
Loading