Circuit breaker berpindah melalui tiga status: Closed, Open, dan Half-Open. Dalam status Closed, permintaan mengalir normal dan kegagalan dihitung; jika kegagalan melampaui ambang batas dalam jendela waktu tertentu, breaker memicu ke Open. Dalam status Open, semua permintaan langsung ditolak tanpa memanggil layanan yang bermasalah; setelah durasi tunggu yang dikonfigurasi, breaker beralih ke Half-Open, di mana sejumlah kecil permintaan uji diizinkan untuk memeriksa apakah layanan downstream sudah pulih.
Artikel ini merekomendasikan dua pilihan: nestjs-resilience4j, yang membungkus konsep pustaka Java Resilience4j ke dalam API asli NestJS dan menyediakan implementasi paling lengkap, serta cockatiel, pustaka ringan berbasis TypeScript yang menawarkan kebijakan Circuit Breaker, Retry, dan Timeout yang dapat disusun bersama. Pilihan bergantung pada seberapa besar bobot yang ingin ditambahkan dan apakah Anda lebih suka API bergaya ekosistem Java atau pendekatan TypeScript-first.
Tiga angka yang paling penting: ambang batas tingkat kegagalan (persentase kegagalan yang memicu breaker, biasanya 50%), durasi tunggu dalam status Open (berapa lama sebelum mencoba Half-Open, biasanya 10–30 detik), dan jumlah panggilan yang diizinkan dalam status Half-Open (biasanya 3–5). Artikel ini menekankan bahwa angka-angka ini harus didasarkan pada pengukuran tingkat kegagalan dan distribusi waktu respons layanan Anda yang sebenarnya — terlalu sensitif dan kesalahan sementara memicu breaker secara tidak perlu; terlalu longgar dan kegagalan cascading merambat sebelum breaker aktif.
Komposisi yang direkomendasikan adalah Timeout → Retry → Circuit Breaker, sehingga timeout dan retry menangani kesalahan sementara sebelum circuit breaker menghitungnya. Jebakan utamanya adalah retry memperkuat sinyal kegagalan: jika Anda mencoba ulang 3 kali per panggilan dan ambang batas kegagalan adalah 5, tiga permintaan yang gagal menghasilkan 9 kejadian kegagalan. Anda harus menyetel jumlah retry dan ambang batas circuit breaker secara bersamaan agar breaker tidak terpicu oleh jumlah kegagalan nyata yang sebenarnya kecil.
Artikel ini menguraikan empat pilihan berdasarkan tingkat kritis bisnis: mengembalikan respons yang di-cache (hasil terakhir yang berhasil, cocok untuk data hanya-baca); mengembalikan default yang aman seperti daftar kosong atau saldo nol; mengembalikan respons terdegradasi dengan data parsial dari sumber alternatif; atau mengembalikan respons kesalahan terstruktur seperti 503 dengan header Retry-After. Artikel ini secara eksplisit memperingatkan agar tidak mengembalikan sukses diam-diam yang menyembunyikan kegagalan dari pemanggil, karena hal ini membuat debugging jauh lebih sulit.
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.