Circuit Breaker
Prevent cascading failures by tracking step failure rates and short-circuiting calls when a threshold is exceeded.
Basic usage
Section titled “Basic usage”import { ok } from 'awaitly';import { createCircuitBreaker, isCircuitOpenError, circuitBreakerPresets,} from 'awaitly/circuit-breaker';
// Create a circuit breaker (name is required)const breaker = createCircuitBreaker('external-api', { failureThreshold: 5, // Open after 5 failures resetTimeout: 30000, // Try again after 30 seconds halfOpenMax: 3, // Allow 3 test requests in half-open state windowSize: 60000, // Count failures within this window (1 minute)});
// Execute with the breakertry { const data = await breaker.execute(async () => { return await fetchFromExternalApi(); }); console.log('Got data:', data);} catch (error) { if (isCircuitOpenError(error)) { console.log(`Circuit is open, retry after ${error.retryAfterMs}ms`); } else { console.log('Operation failed:', error); }}Using presets
Section titled “Using presets”import { createCircuitBreaker, circuitBreakerPresets } from 'awaitly/circuit-breaker';
// For critical services - opens quickly, recovers slowlyconst criticalBreaker = createCircuitBreaker( 'payment-api', circuitBreakerPresets.critical);
// For lenient services - tolerates more failuresconst lenientBreaker = createCircuitBreaker( 'recommendation-api', circuitBreakerPresets.lenient);Result-returning operations
Section titled “Result-returning operations”Use executeResult instead of execute if your operation returns a Result:
const result = await breaker.executeResult(async () => { return ok(await fetchFromExternalApi());});
if (!result.ok) { if (isCircuitOpenError(result.error)) { console.log('Circuit is open, try again later'); }}Circuit states
Section titled “Circuit states”The circuit breaker has three states:
| State | Description |
|---|---|
CLOSED | Normal operation. Failures are tracked. |
OPEN | Circuit tripped. All calls fail immediately. |
HALF_OPEN | Testing recovery. Limited calls allowed. |
const stats = breaker.getStats();console.log(stats.state); // 'CLOSED' | 'OPEN' | 'HALF_OPEN'console.log(stats.failureCount); // Failures in current windowconsole.log(stats.successCount); // Successes in current windowconsole.log(stats.halfOpenSuccesses); // Successful test callsState transitions
Section titled “State transitions”CLOSED → OPEN: When failureThreshold is reachedOPEN → HALF_OPEN: After resetTimeout elapsesHALF_OPEN → CLOSED: After halfOpenMax successesHALF_OPEN → OPEN: On any failureIn workflows
Section titled “In workflows”const breaker = createCircuitBreaker('api', circuitBreakerPresets.standard);
const result = await workflow.run(async ({ step }) => { const data = await step('fetchData', async () => { const apiResult = await breaker.executeResult(() => ok(await externalApi.fetch()) ); return apiResult; });
return data;});Multiple services
Section titled “Multiple services”Create separate breakers for independent failure domains:
const paymentBreaker = createCircuitBreaker('payment', { failureThreshold: 3, resetTimeout: 60000, // Conservative for payments});
const inventoryBreaker = createCircuitBreaker('inventory', { failureThreshold: 10, resetTimeout: 10000, // Can recover faster});
const emailBreaker = createCircuitBreaker('email', { failureThreshold: 20, resetTimeout: 5000, // Non-critical, aggressive recovery});Configuration reference
Section titled “Configuration reference”{ failureThreshold: number; // Failures before opening (required) resetTimeout: number; // Ms before half-open (required) halfOpenMax?: number; // Test calls allowed (default: 1) windowSize?: number; // Failure counting window (default: 60000)}