Blog Hoch
Feb 2026·2 tup leghDe'wI' chenmoHta'

Circuit Breaker Pattern resilient services vaD

#backend#reliability#distributed-systems

downstream service fail ghoS vaj naive response (request Hoch retry) wrong move. retry Hoch request pile on struggling service, 'ej caller timeout wait thread pagh connection (service SoH pIm lo'laHbe'). circuit breaker pattern 'oH stop: failures law' qaSDI' requests nob Qo' poH, fail fast (fail slow pagh).

wej states

circuit breaker state machine wej states (electrical analogy):

  • Closed - normal operation. requests downstream service Daq pass. failures counted.
  • Open - failure threshold crossed. requests immediately fail (call attempt pagh), cooldown period.
  • Half-open - cooldown qaSDI' wa' (pagh few) test requests pass. succeed vaj breaker close. fail vaj reopen, cooldown restart.
CLOSED --(too many failures)--> OPEN --(cooldown elapses)--> HALF_OPEN
   ^                                                              |
   |______________________(test request succeeds)_________________|
                            (test request fails) -----> OPEN

key insight "open" state: caller Hoch timeout wait pagh. breaker immediately cheaply fail, resources free, downstream service recover room (load pagh).

minimal implementation

class CircuitBreaker {
  constructor(fn, { failureThreshold = 5, cooldownMs = 30_000 } = {}) {
    this.fn = fn;
    this.failureThreshold = failureThreshold;
    this.cooldownMs = cooldownMs;
    this.state = 'CLOSED';
    this.failureCount = 0;
    this.openedAt = null;
  }

  async call(...args) {
    if (this.state === 'OPEN') {
      if (Date.now() - this.openedAt >= this.cooldownMs) {
        this.state = 'HALF_OPEN';
      } else {
        throw new Error('Circuit is open — failing fast');
      }
    }

    try {
      const result = await this.fn(...args);
      this.#onSuccess();
      return result;
    } catch (err) {
      this.#onFailure();
      throw err;
    }
  }

  #onSuccess() {
    this.failureCount = 0;
    this.state = 'CLOSED';
  }

  #onFailure() {
    this.failureCount++;
    if (this.state === 'HALF_OPEN' || this.failureCount >= this.failureThreshold) {
      this.state = 'OPEN';
      this.openedAt = Date.now();
    }
  }
}

lo' - risky call function wrap:

const breaker = new CircuitBreaker(
  (orderId) => fetch(`https://payments.internal/charge/${orderId}`).then((r) => r.json()),
  { failureThreshold: 5, cooldownMs: 30_000 },
);

try {
  const result = await breaker.call(order.id);
} catch (err) {
  // Either the payment call actually failed, or the breaker is open —
  // either way, don't retry synchronously in the same request.
  return res.status(503).json({ error: 'Payment service unavailable, try again shortly' });
}

legh: HALF_OPEN Daq wa' failed call breaker immediately re-open (full threshold again poQ pagh) - half-open probe caution; wa' bad test result service ready pagh conclude.

circuit breaker vs retry vs timeout

wej confuse ("downstream call Qu'be'" respond) 'ach pIm Qagh solve:

  • Timeout single call wait bound.
  • Retry failed call re-attempt (backoff), failure transient assume.
  • Circuit breaker failures calls law' time Daq track, service systemically broken vaj calls attempt stop (transiently unlucky pagh).

compose: call timeout Daq wrap, bounded retries, retry logic circuit breaker qaSDI' - systemically down service (timeout + retries) × request Hoch hammer pagh.

failure threshold nuq count

raw failures unbounded time count wrong - 5 failures 5 requests minute Daq real problem; 5 failures 10,000 requests week noise. sliding window (last N requests pagh N seconds) practical:

class SlidingWindowBreaker {
  constructor(fn, { failureRateThreshold = 0.5, windowSize = 20, cooldownMs = 30_000 } = {}) {
    this.fn = fn;
    this.failureRateThreshold = failureRateThreshold;
    this.windowSize = windowSize;
    this.cooldownMs = cooldownMs;
    this.state = 'CLOSED';
    this.results = []; // rolling window of true/false (success/failure)
    this.openedAt = null;
  }

  #recordResult(success) {
    this.results.push(success);
    if (this.results.length > this.windowSize) this.results.shift();
  }

  #failureRate() {
    if (this.results.length < this.windowSize) return 0; // not enough data yet
    const failures = this.results.filter((r) => !r).length;
    return failures / this.results.length;
  }

  async call(...args) {
    if (this.state === 'OPEN') {
      if (Date.now() - this.openedAt < this.cooldownMs) {
        throw new Error('Circuit is open — failing fast');
      }
      this.state = 'HALF_OPEN';
    }

    try {
      const result = await this.fn(...args);
      this.#recordResult(true);
      if (this.state === 'HALF_OPEN') this.state = 'CLOSED';
      return result;
    } catch (err) {
      this.#recordResult(false);
      if (this.state === 'HALF_OPEN' || this.#failureRate() >= this.failureRateThreshold) {
        this.state = 'OPEN';
        this.openedAt = Date.now();
      }
      throw err;
    }
  }
}

breaker nuqDaq

breaker per downstream dependency (global pagh) - failing recommendations service payments provider calls gate breaker trip Qo'. external dependencies law' service: dependency per breaker instance, thresholds dependency actual failure characteristics rap (flaky third-party API lower threshold longer cooldown vs internal service).

yaj

circuit breaker failures prevent pagh - service downstream failure worse (load hammer) Qotlh 'ej resources waste (calls likely fail) Qotlh. state machine simple; thresholds 'ej window sizes dependency actual failure rap choose (round numbers neH pagh).

chu'Optimistic vs Pessimistic Locking databases DaqqanFeature Flags: Qu' lIj