Tanpa circuit breaker, kegagalan cascading dapat meruntuhkan seluruh platform Anda dalam waktu kurang dari 90 detik. Michael Nygard mempopulerkan pola circuit breaker dalam bukunya Release It! (2018) untuk mencegah skenario ini: ketika Layanan A bergantung pada Layanan B, dan B mulai timeout, thread A menumpuk menunggu respons, menghabiskan pool koneksinya, yang menyebabkan A gagal, yang mengalir ke Layanan C yang bergantung pada A. Pola circuit breaker memutus rantai ini.
Circuit breaker adalah mesin status dengan tiga status. Closed (operasi normal): permintaan mengalir melalui, kegagalan dihitung. Jika kegagalan melebihi ambang batas dalam jendela waktu, breaker memicu ke Open. Open (mode kegagalan): semua permintaan segera ditolak dengan kesalahan. Setelah timeout yang dikonfigurasi, breaker pindah ke Half-Open. Half-Open (probe): sejumlah terbatas permintaan uji diizinkan melalui. Jika berhasil, breaker menutup. Jika gagal, ia kembali ke Open.
Konfigurasi circuit breaker memerlukan tiga angka kunci: ambang batas tingkat kegagalan (persentase kegagalan yang memicu breaker — biasanya 50%), durasi tunggu dalam status terbuka (berapa lama menunggu sebelum mencoba half-open — biasanya 10-30 detik), dan panggilan yang diizinkan dalam status half-open (berapa banyak permintaan uji yang diizinkan — biasanya 3-5).
Circuit Breaker State Machine
Failure rate > 50%
┌──────────┐ ───────────────────► ┌──────────┐
│ CLOSED │ │ OPEN │
│(normal) │ ◄─────────────────── │(rejected)│
└──────────┘ Success in half-open└────┬─────┘
│ │
│ Requests pass through │ All requests rejected
│ Failures counted │ No calls to downstream
│ │
│ After 30s timeout
│ │
│ ┌──────▼──────┐
│ ◄─ 5 test requests ok ── │ HALF-OPEN │
│ 2 fail → back to Open │ (probing) │
└─────────────┘
Cascading Failure WITHOUT Circuit Breaker:
Service A times out (30s) → thread pool exhausted
→ Service B calls A → B times out → B thread pool exhausted
→ Service C calls B → C fails
ENTIRE PLATFORM DOWN in < 90 seconds
WITH Circuit Breaker:
A fails → CB opens → B gets immediate error → B uses fallback
→ C continues normally with cached/degraded responseDari membangun alur kerja persetujuan multi-tingkat di ERP Commsult: bungkus panggilan layanan eksternal (penyedia email, generator PDF, payment gateway) dengan circuit breaker, tetapi bukan panggilan layanan internal yang berbagi database yang sama. Circuit breaker adalah untuk melindungi dari kegagalan tingkat jaringan ke dependensi eksternal atau jarak jauh.
Implementasi circuit breaker yang paling lengkap untuk NestJS berasal dari nestjs-resilience4j, yang membungkus konsep pustaka Resilience4j Java dalam API asli NestJS. Alternatifnya, pustaka cockatiel menyediakan implementasi TypeScript-native ringan dengan kebijakan Circuit Breaker, Retry, dan Timeout yang tersusun dengan baik.
// Using 'cockatiel' — lightweight TypeScript circuit breaker
import { CircuitBreakerPolicy, timeout, TimeoutStrategy, retry, ExponentialBackoff } from 'cockatiel';
// Compose: Timeout → Retry → Circuit Breaker
const circuitBreaker = new CircuitBreakerPolicy({
halfOpenAfter: 30_000, // 30s before probing
breaker: consecutivelyFailed(5), // open after 5 consecutive failures
});
const retryPolicy = retry(handleAll, {
maxAttempts: 3,
backoff: new ExponentialBackoff(),
});
const timeoutPolicy = timeout(5_000, TimeoutStrategy.Cooperative); // 5s
// email.service.ts — wrapping external SMTP API
@Injectable()
export class EmailService {
private policy = circuitBreaker.wrap(retryPolicy.wrap(timeoutPolicy));
async sendInvoice(data: InvoiceEmailData): Promise<void> {
try {
await this.policy.execute(() =>
this.smtpClient.send({
to: data.recipientEmail,
subject: 'Invoice Ready',
html: this.templateService.render('invoice', data),
})
);
} catch (error) {
// Circuit is open OR all retries failed
// Log and queue for later retry via BullMQ
this.logger.error('Email send failed, queuing for retry', { error, data });
await this.retryQueue.add('email:retry', data, { delay: 60_000 });
}
}
}
// Health endpoint — expose circuit state
@Get('/health/circuits')
getCircuitHealth() {
return {
emailService: circuitBreaker.state, // 'closed' | 'open' | 'half-open'
pdfService: pdfCircuitBreaker.state,
paymentGateway: paymentCircuitBreaker.state,
};
}Circuit breaker bekerja paling baik dalam kombinasi dengan kebijakan retry dan timeout. Komposisi tipikal: Timeout (gagal cepat jika respons terlalu lama) → Retry (coba ulang pada kesalahan sementara) → Circuit Breaker (trip jika terlalu banyak retry gagal). Berhati-hatilah dengan interaksi retry + circuit breaker: retry memperkuat sinyal kegagalan.
Circuit breaker terbuka berarti ada sesuatu yang salah di downstream — tetapi itu juga berarti layanan Anda secara diam-diam menjatuhkan permintaan. Tanpa pemantauan, Anda akan menemukan circuit breaker terbuka ketika pengguna mengeluh permintaan mereka 'hanya mengembalikan kesalahan secara instan.' Instrumentasikan setiap circuit breaker dengan metrik: lacak transisi status, jumlah penolakan, dan tingkat kesalahan yang memicu trip.
Ketika circuit breaker menolak permintaan, apa yang harus dikembalikan layanan Anda? Pilihan: (1) Respons yang di-cache — kembalikan respons terakhir yang berhasil dari cache; (2) Respons default — kembalikan default yang aman (daftar kosong, saldo nol); (3) Respons terdegradasi — kembalikan data parsial dari sumber yang berbeda dan tersedia; (4) Respons kesalahan — kembalikan kesalahan terstruktur dengan konteks.
Satu deployment yang kuat adalah menempatkan circuit breaker di API gateway Anda — gateway melacak kesehatan setiap layanan downstream dan memicu breaker ketika tingkat kesalahan layanan melonjak. Ini memberikan perlindungan di perimeter tanpa memerlukan setiap layanan untuk mengimplementasikan breaker mereka sendiri.
Circuit breaker tidak berguna jika Anda tidak dapat memverifikasi bahwa mereka bekerja. Tulis tes integrasi yang menyuntikkan kegagalan ke dalam dependensi downstream (mock panggilan HTTP untuk melempar kesalahan) dan verifikasi: (1) breaker bertransisi ke Open setelah ambang batas; (2) permintaan segera ditolak dalam status Open; (3) breaker bertransisi ke Half-Open setelah durasi tunggu; (4) breaker menutup setelah permintaan uji yang berhasil dalam Half-Open.