Resilience Patterns — Cheat Sheet

Tag: architecture, microservices, resilience, circuit-breaker, retry, distributed-systems

Livello: intermedio

TL;DR: Pattern per prevenire guasti a catena e garantire continuita' operativa nei sistemi distribuiti.

Concetti chiave

Circuit Breaker isola i servizi degradati evitando sovraccarichi continui.

Stati del Circuit Breaker: Closed (normale), Open (blocco chiamate), Half-Open (test ripristino).

Retry gestisce errori transitori con tentativi ripetuti e distanziati.

Exponential Backoff con Jitter riduce la contesa introducendo ritardi casuali crescenti.

Bulkhead isola risorse e pool di thread per evitare crash totali.

Timeout interrompe chiamate bloccanti prevenendo l'esaurimento delle risorse locali.

Fallback fornisce una risposta degradata o predefinita in caso di errore.

Rate Limiting e Throttling proteggono i nodi downstream da picchi imprevisti.

Idempotenza e' requisito fondamentale per applicare retry in sicurezza.

Domande tipiche e risposte pronte

Q: Come decidi quando applicare un Retry rispetto a un Circuit Breaker?R: Uso il Retry per fallimenti transitori e brevi come glitch di rete; applico il Circuit Breaker quando i fallimenti sono sistematici, per dare tempo al downstream di recuperare senza saturare risorse.

Q: Perche' si aggiunge il Jitter all'Exponential Backoff?R: Evita il problema del Thundering Herd, impedendo a molti client sincronizzati di colpire il servizio simultaneamente allo stesso intervallo.

Q: Come garantisci la sicurezza delle chiamate ripetute con Retry?R: Mi assicuro che le operazioni siano idempotenti, usando idempotency key univoche per prevenire duplicazioni di effetti collaterali su scritture e pagamenti.

Insidie e errori comuni

Retry storm causato da tentativi su errori permanenti tipo 400 o 404.

Assenza di timeout reattivi che blocca thread pool e causa memory leak.

Fallback che chiama a sua volta servizi instabili propagando l'errore.

Soglie del Circuit Breaker tarate senza considerare il volume reale di traffico.

Retry eseguiti su endpoint non idempotenti causando mutazioni duplicate.

Snippet e comandiTypeScript

// Esempio logica Circuit Breaker essenziale
class SimpleCircuitBreaker {
  private failures = 0;
  private state: 'CLOSED' | 'OPEN' | 'HALF_OPEN' = 'CLOSED';
  private nextAttempt = Date.now();

  constructor(private threshold = 5, private timeoutMs = 10000) {}

  async execute<T>(action: () => Promise<T>): Promise<T> {
    if (this.state === 'OPEN') {
      if (Date.now() < this.nextAttempt) throw new Error('Circuit is OPEN');
      this.state = 'HALF_OPEN';
    }
    try {
      const res = await action();
      this.reset();
      return res;
    } catch (err) {
      this.recordFailure();
      throw err;
    }
  }

  private recordFailure() {
    this.failures++;
    if (this.failures >= this.threshold || this.state === 'HALF_OPEN') {
      this.state = 'OPEN';
      this.nextAttempt = Date.now() + this.timeoutMs;
    }
  }

  private reset() {
    this.failures = 0;
    this.state = 'CLOSED';
  }
}

Da ripassare

[ ] Taratura soglie di failure rate e finestre temporali nel Circuit Breaker

[ ] Implementazione di token bucket e leaky bucket per il Rate Limiting

[x] Concetto di isolamento tramite Bulkhead su thread pool distinti

[x] Differenza tra errori transitori (503, timeout) ed errori permanenti (4xx)

Jarvis Interviews
Jarvis