circuit-breaker
Skill0 starsChanged 34 days agoMITView on GitHub
Implementing the circuit breaker pattern to stop cascading failures: builds the three-state machine (closed, open, half-open), wires it around outbound HTTP calls and database queries, tunes failure thresholds and recovery timeouts, exposes state as a metric, and integrates with existing retry and timeout logic. Use when you want to add a circuit breaker, prevent cascading failures, fail fast when a dependency is down, implement the circuit breaker pattern, stop hammering a failing service, add resilience to outbound calls, or use a library like opossum or resilience4j.
- Reads credentials
1 other project uses a version of this.
Adopted by
Most recently taken up by
- prkash1704/claude-skillstheir own version, first seen Jun 24, 2026
Other versions1 project uses a version of this.
Word for word · 0
None found.
Edited · 0
None found.
Wrote their own version · 1
- prkash1704/claude-skills★ 0same name
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository68 other files it gives its agents.
- AGENTS.mdAGENTS.md
- .claude/CLAUDE.mdCLAUDE.md
- accessibilitySkill
- api-designSkill
- api-versioningSkill
- authSkill
- background-jobsSkill
- cachingSkill
- capacity-planningSkill
- changelogSkill
- chaos-engineeringSkill
- ci-cdSkill
- claude-best-practicesSkill
- code-commentsSkill
- code-hygieneSkill
- code-reviewSkill
- code-structureSkill
- commit-styleSkill
- concurrency-and-lockingSkill
- database-transactionsSkill
- data-consistencySkill
- debug-infra-startupSkill
- debug-sessionSkill
- demo-video-creatorSkill
- demo-video-script-creatorSkill
- dependency-injectionSkill
- dependency-updateSkill
- disk-cleanupSkill
- distributed-tracingSkill
- dockerSkill
- env-configSkill
- env-setupSkill
- error-handlingSkill
- event-driven-architectureSkill
- feature-flagsSkill
- file-storageSkill
- graceful-shutdownSkill
- idempotencySkill
- input-validationSkill
- jars-engineering-blogsmithSkill
- jars-workshop-builderSkill
- licensingSkill
- loggingSkill
- memory-and-allocationsSkill
- migrate-dbSkill
- monitoringSkill
- multi-tenancySkill
- notificationsSkill
- openapiSkill
- paginationSkill
- perf-profileSkill
- pr-descriptionSkill
- query-optimizationSkill
- rate-limitingSkill
- refactorSkill
- releaseSkill
- runbook-writerSkill
- searchSkill
- secrets-auditSkill
- simplifySkill
- The first 60 shown.
The file
We split this skill into the rules it gives an agent
4 rules. About 0 are common, 2 are distinctive, and 2 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈2
Distinctive · estimate
This author's own choices. Worth a look.
2
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 286
Set
call_timeoutfrom real p99 latency data, not guesses: Check your monitoring dashboard for the dependency's p99 over the last 30 days. - Distinctive: line 335
Always log state transitions at the right level: OPEN = error, HALF-OPEN = warning, CLOSED = info.
- Very specific: line 339
Never catch CircuitOpenError inside the same layer that makes the call.
- Distinctive: line 341
Test the open state explicitly — most teams only test the happy path.
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
circuit-breaker
Wrap every outbound call that can fail. Fail fast when a dependency is struggling — never let one slow dependency drain your entire thread pool.
1. The three-state machine
failure threshold reached
CLOSED ──────────────────────────────► OPEN
▲ │
│ success │ reset timeout expires
│ ▼
└──────────────────────────────── HALF-OPEN
probe succeeds │
│ probe fails
▼
OPEN (reset timeout restarts)- CLOSED — normal operation; requests pass through; failures counted
- OPEN — dependency is unhealthy; requests fail immediately without trying; no load on the dependency
- HALF-OPEN — recovery probe; one request allowed through; if it succeeds → CLOSED, if it fails → OPEN again
2. Node.js — opossum (battle-tested library)
// lib/circuitBreakers.ts
/**
* lib/circuitBreakers.ts
* ----------------------
* Circuit breaker instances for each outbound dependency.
* One breaker per logical dependency — not per endpoint.
*
* Depends on: opossum, lib/logger.ts, lib/metrics.ts
* Used by: services that make outbound calls
*/
import CircuitBreaker from 'opossum';
import { logger } from './logger';
export interface BreakerOptions {
timeout?: number; // ms before a single call is considered failed (default 3000)
errorThresholdPct?: number; // % failures to trip open (default 50)
resetTimeout?: number; // ms to wait before probing in half-open (default 30000)
volumeThreshold?: number; // min calls before threshold applies (default 5)
}
export function createBreaker<T extends unknown[], R>(
name: string,
fn: (...args: T) => Promise<R>,
opts: BreakerOptions = {},
): CircuitBreaker<T, R> {
const breaker = new CircuitBreaker(fn, {
name,
timeout: opts.timeout ?? 3_000,
errorThresholdPercentage: opts.errorThresholdPct ?? 50,
resetTimeout: opts.resetTimeout ?? 30_000,
volumeThreshold: opts.volumeThreshold ?? 5,
});
// Log every state transition — critical for incident debugging
breaker.on('open', () => logger.error(`Circuit breaker OPEN. name=${name} action=failing_fast`));
breaker.on('halfOpen', () => logger.warning(`Circuit breaker HALF-OPEN. name=${name} action=probing`));
breaker.on('close', () => logger.info(`Circuit breaker CLOSED. name=${name} action=recovered`));
breaker.on('fallback', (result) => logger.warning(`Circuit breaker fallback used. name=${name}`));
breaker.on('timeout', () => logger.warning(`Circuit breaker timeout. name=${name}`));
return breaker;
}// lib/stripeClient.ts
/**
* lib/stripeClient.ts
* -------------------
* Stripe API calls wrapped in a circuit breaker.
* Falls back to a queued-retry on open circuit.
*
* Depends on: lib/circuitBreakers.ts, lib/queue.ts
* Used by: services/paymentService.ts
*/
import Stripe from 'stripe';
import { createBreaker } from './circuitBreakers';
import { enqueueRetry } from './queue';
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);
// Wrap the outbound call — not the service method
async function _chargeCard(amount: number, currency: string, paymentMethodId: string) {
return stripe.paymentIntents.create({ amount, currency, payment_method: paymentMethodId });
}
export const stripeBreaker = createBreaker('stripe', _chargeCard, {
timeout: 5_000, // Stripe p99 is ~2 s — 5 s gives headroom
errorThresholdPct: 30, // trip after 30 % failures (payment is critical — trip early)
resetTimeout: 60_000, // wait 60 s before probing
volumeThreshold: 3, // trip after just 3 calls (low traffic service)
});
// Fallback — enqueue for retry rather than returning an error to the user
stripeBreaker.fallback(async (amount, currency, paymentMethodId) => {
await enqueueRetry('stripe-charge', { amount, currency, paymentMethodId });
throw new Error('Payment service temporarily unavailable — request queued for retry');
});
export async function chargeCard(
amount: number,
currency: string,
paymentMethodId: string,
) {
return stripeBreaker.fire(amount, currency, paymentMethodId);
}3. Python — manual implementation (no heavyweight framework needed)
# lib/circuit_breaker.py
"""
lib/circuit_breaker.py
----------------------
Minimal, production-ready circuit breaker implementation.
Thread-safe using asyncio.Lock for async code.
Depends on: lib/logger.py
Used by: any service making outbound HTTP or DB calls
"""
import asyncio
import time
from enum import Enum
from lib.logger import get_logger
logger = get_logger(__name__)
class State(Enum):
CLOSED = "closed"
OPEN = "open"
HALF_OPEN = "half_open"
class CircuitBreaker:
def __init__(
self,
name: str,
failure_threshold: int = 5, # failures before opening
success_threshold: int = 2, # successes in half-open before closing
reset_timeout_s: float = 30.0, # seconds before probing
call_timeout_s: float = 3.0, # per-call timeout
) -> None:
self.name = name
self.failure_threshold = failure_threshold
self.success_threshold = success_threshold
self.reset_timeout_s = reset_timeout_s
self.call_timeout_s = call_timeout_s
self._state = State.CLOSED
self._failure_count = 0
self._success_count = 0
self._opened_at: float = 0.0
self._lock = asyncio.Lock()
@property
def state(self) -> State:
return self._state
async def call(self, fn, *args, **kwargs):
"""Execute fn through the circuit breaker."""
async with self._lock:
if self._state == State.OPEN:
if time.monotonic() - self._opened_at >= self.reset_timeout_s:
self._transition(State.HALF_OPEN)
else:
raise CircuitOpenError(f"Circuit breaker OPEN: {self.name}")
try:
result = await asyncio.wait_for(fn(*args, **kwargs), timeout=self.call_timeout_s)
await self._on_success()
return result
except CircuitOpenError:
raise
except Exception as exc:
await self._on_failure(exc)
raise
async def _on_success(self) -> None:
async with self._lock:
if self._state == State.HALF_OPEN:
self._success_count += 1
if self._success_count >= self.success_threshold:
self._transition(State.CLOSED)
elif self._state == State.CLOSED:
self._failure_count = 0 # reset on success
async def _on_failure(self, exc: Exception) -> None:
async with self._lock:
self._failure_count += 1
logger.warning(
"Circuit breaker recorded failure. name=%s failures=%d error=%s",
self.name, self._failure_count, exc,
)
if self._state in (State.CLOSED, State.HALF_OPEN):
if self._failure_count >= self.failure_threshold:
self._transition(State.OPEN)
def _transition(self, new_state: State) -> None:
old = self._state
self._state = new_state
if new_state == State.OPEN:
self._opened_at = time.monotonic()
self._failure_count = 0
self._success_count = 0
logger.error("Circuit breaker OPEN. name=%s action=failing_fast", self.name)
elif new_state == State.HALF_OPEN:
self._failure_count = 0
self._success_count = 0
logger.warning("Circuit breaker HALF-OPEN. name=%s action=probing", self.name)
elif new_state == State.CLOSED:
self._failure_count = 0
self._success_count = 0
logger.info("Circuit breaker CLOSED. name=%s action=recovered", self.name)
class CircuitOpenError(Exception):
"""Raised when a call is rejected because the circuit is open."""# Usage in a service
from lib.circuit_breaker import CircuitBreaker, CircuitOpenError
_stripe_breaker = CircuitBreaker(
name="stripe",
failure_threshold=5,
reset_timeout_s=60.0,
call_timeout_s=5.0,
)
async def charge_card(amount: int, payment_method_id: str):
try:
return await _stripe_breaker.call(
stripe_client.charge,
amount=amount,
payment_method_id=payment_method_id,
)
except CircuitOpenError:
logger.warning(
"Stripe circuit open — queuing charge for retry. "
"amount=%d payment_method_id=%s",
amount, payment_method_id,
)
await enqueue_retry("stripe-charge", {"amount": amount, "pmid": payment_method_id})
raise PaymentUnavailableError("Payment temporarily unavailable")4. Tune the thresholds — defaults are not production values
| Parameter | Too low | Too high | Starting point |
failure_threshold | Trips on normal transient errors | Allows long degradation before tripping | 5 failures |
error_threshold_pct | Trips during traffic spikes with 1–2 failures | Allows high failure rates | 50 % |
reset_timeout | Re-opens immediately, thrashes | Long outage window for callers | 30–60 s |
call_timeout | Times out healthy slow calls | Hangs thread pool | p99 latency × 2 |
volume_threshold | Ignores initial failures | Takes too long to trip | 5–10 calls |
Set call_timeout from real p99 latency data, not guesses. Check your monitoring
dashboard for the dependency's p99 over the last 30 days. Multiply by 2. That is your
starting timeout.
5. Expose circuit state as a metric
The circuit breaker state must be observable. When it opens in production, the on-call engineer needs to know immediately.
// Add to your Prometheus metrics
import { Gauge } from 'prom-client';
const breakerState = new Gauge({
name: 'circuit_breaker_state',
help: 'Circuit breaker state: 0=closed, 1=half_open, 2=open',
labelNames: ['name'],
});
// opossum exposes stats — sync to gauge every 10 s
setInterval(() => {
const state = stripeBreaker.opened ? 2 : stripeBreaker.halfOpen ? 1 : 0;
breakerState.set({ name: 'stripe' }, state);
}, 10_000);Alert when circuit_breaker_state{name="stripe"} == 2 for more than 60 s.
A breaker that stays open for more than one reset cycle needs human attention.
6. What NOT to wrap in a circuit breaker
| Do wrap | Do not wrap |
| Outbound HTTP to third-party APIs | In-memory function calls |
| Database queries on a separate host | Redis reads on localhost |
| gRPC calls to other services | Idempotent cache reads |
| External email / SMS providers | Synchronous CPU-bound work |
Circuit breakers add latency overhead on every call. Use them at service boundaries where failure is external and observable, not inside a single process.
Rules
- One circuit breaker per logical dependency, not per endpoint. All Stripe calls share one breaker — if Stripe is down, all Stripe calls should fail fast together.
- Always log state transitions at the right level: OPEN = error, HALF-OPEN = warning, CLOSED = info.
- Set a fallback before the breaker opens in production. An open breaker with no fallback returns a raw error to the user — a fallback returns a graceful degradation.
- Never catch
CircuitOpenErrorinside the same layer that makes the call. Let it propagate to the service boundary so the caller can decide the fallback. - Test the open state explicitly — most teams only test the happy path.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

