feat(gateway): add backoff calculation utilities
This commit is contained in:
parent
4583f88626
commit
5a3f915641
48
src/cli/gateway-cli/backoff.test.ts
Normal file
48
src/cli/gateway-cli/backoff.test.ts
Normal file
@ -0,0 +1,48 @@
|
||||
// src/cli/gateway-cli/backoff.test.ts
|
||||
import { describe, it, expect } from "vitest";
|
||||
import { calculateBackoffMs, applyJitter } from "./backoff.js";
|
||||
|
||||
describe("calculateBackoffMs", () => {
|
||||
it("returns 0 for zero consecutive failures", () => {
|
||||
expect(calculateBackoffMs(0)).toBe(0);
|
||||
});
|
||||
|
||||
it("returns 2000ms for first failure", () => {
|
||||
expect(calculateBackoffMs(1)).toBe(2000);
|
||||
});
|
||||
|
||||
it("returns 4000ms for second failure", () => {
|
||||
expect(calculateBackoffMs(2)).toBe(4000);
|
||||
});
|
||||
|
||||
it("returns 32000ms for fifth failure", () => {
|
||||
expect(calculateBackoffMs(5)).toBe(32000);
|
||||
});
|
||||
|
||||
it("caps at 60000ms for high failure counts", () => {
|
||||
expect(calculateBackoffMs(10)).toBe(60000);
|
||||
expect(calculateBackoffMs(100)).toBe(60000);
|
||||
});
|
||||
});
|
||||
|
||||
describe("applyJitter", () => {
|
||||
it("returns 0 for 0 input", () => {
|
||||
expect(applyJitter(0)).toBe(0);
|
||||
});
|
||||
|
||||
it("returns value within +/- 10% of input", () => {
|
||||
const input = 10000;
|
||||
const minExpected = Math.floor(input * 0.9);
|
||||
const maxExpected = Math.ceil(input * 1.1);
|
||||
for (let i = 0; i < 100; i++) {
|
||||
const result = applyJitter(input);
|
||||
expect(result).toBeGreaterThanOrEqual(minExpected);
|
||||
expect(result).toBeLessThanOrEqual(maxExpected);
|
||||
}
|
||||
});
|
||||
|
||||
it("returns an integer", () => {
|
||||
const result = applyJitter(2000);
|
||||
expect(Number.isInteger(result)).toBe(true);
|
||||
});
|
||||
});
|
||||
30
src/cli/gateway-cli/backoff.ts
Normal file
30
src/cli/gateway-cli/backoff.ts
Normal file
@ -0,0 +1,30 @@
|
||||
// src/cli/gateway-cli/backoff.ts
|
||||
|
||||
const BASE_BACKOFF_MS = 2000;
|
||||
const MAX_BACKOFF_MS = 60_000;
|
||||
const JITTER_FACTOR = 0.1; // +/- 10%
|
||||
|
||||
/**
|
||||
* Calculate exponential backoff based on consecutive failures.
|
||||
* Formula: min(BASE * 2^(failures-1), MAX)
|
||||
*
|
||||
* @param consecutiveFailures - Number of consecutive failures (0 = no backoff)
|
||||
* @returns Backoff duration in milliseconds
|
||||
*/
|
||||
export function calculateBackoffMs(consecutiveFailures: number): number {
|
||||
if (consecutiveFailures <= 0) return 0;
|
||||
const exponential = BASE_BACKOFF_MS * Math.pow(2, consecutiveFailures - 1);
|
||||
return Math.min(exponential, MAX_BACKOFF_MS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply random jitter to a backoff value to prevent thundering herd.
|
||||
*
|
||||
* @param baseMs - Base backoff in milliseconds
|
||||
* @returns Backoff with +/- 10% jitter applied, rounded to integer
|
||||
*/
|
||||
export function applyJitter(baseMs: number): number {
|
||||
if (baseMs <= 0) return 0;
|
||||
const jitter = (Math.random() - 0.5) * 2 * JITTER_FACTOR * baseMs;
|
||||
return Math.round(baseMs + jitter);
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user