Circuit Breaker Membatasi Panggilan ke Dependency yang Bermasalah

Dependency remote dapat mengalami kegagalan yang menetap sekaligus mahal. Koneksi mencapai timeout, worker slot tetap terpakai, request queue membesar, dan retry menambah traffic ke service yang sedang tidak mampu merespons. Caller yang terus mengirim kelas request yang sama dapat mengubah satu kegagalan dependency menjadi tekanan pada process miliknya sendiri.

Circuit breaker menempatkan keputusan admission yang memiliki state di depan panggilan tersebut. Selama dependency beroperasi dalam batas policy, panggilan diteruskan. Setelah kegagalan yang memenuhi kriteria melewati ambang, breaker menjadi open dan menolak panggilan baru secara lokal selama periode tertentu. Pemulihan diuji dengan traffic terbatas, bukan dengan langsung mengembalikan seluruh load normal.

Mekanisme ini sengaja lebih sempit daripada resilience layer umum. Circuit breaker tidak memperbaiki dependency, tidak membuat retry menjadi aman, dan tidak menentukan fallback response. Tugasnya adalah membatasi remote work yang sia-sia sambil mempertahankan jalur terkontrol untuk kembali melayani traffic.

State closed mencatat hasil

Breaker biasanya dimulai dalam state closed. Panggilan mencapai dependency dan hasilnya masuk ke failure policy.

request
   |
   v
[ closed breaker ] ---> dependency
         |
         +--- catat hasil

Policy dapat memakai kegagalan beruntun, failure ratio dalam rolling window, atau pengukuran terbatas lain. Sebuah ratio biasanya memerlukan jumlah sample minimum; membuka breaker setelah satu kegagalan dalam window yang hanya berisi satu request akan menghasilkan ratio yang secara matematis tinggi tetapi terlalu sensitif secara operasional.

Tidak semua hasil aplikasi yang gagal layak masuk ke signal breaker. 404 yang valid untuk object yang tidak ada mungkin tidak menyatakan apa pun tentang kesehatan dependency. Connection refusal, transport timeout, atau response 5xx tertentu dapat menjadi kandidat yang lebih kuat. Klasifikasinya harus sesuai dengan contract dependency.

Panggilan lambat juga dapat relevan meskipun akhirnya sukses. Sebagian implementasi melacak latency atau slow-call ratio karena service yang tersaturasi dan menyelesaikan request tepat sebelum timeout masih dapat menghabiskan sebagian besar kapasitas caller. Policy tersebut memerlukan threshold eksplisit, bukan memperlakukan semua response non-error sebagai kondisi yang setara.

State open gagal secara lokal

Setelah threshold yang dikonfigurasi terlampaui, breaker berpindah ke open. Panggilan yang berada dalam scope breaker tidak lagi mencoba operasi remote sampai interval open mengizinkan pemeriksaan pemulihan.

application ---> [ open breaker ] -X-> dependency
                      |
                      +--> kegagalan lokal

Perubahan ini menurunkan biaya kegagalan. Penolakan lokal tidak menghabiskan connection attempt atau menunggu dependency timeout. Hal ini juga dapat mencegah armada caller terus menerapkan traffic ke endpoint yang memiliki peluang kecil untuk melayaninya.

State open bukan bukti bahwa dependency tidak tersedia secara global. Itu adalah kesimpulan lokal berdasarkan hasil yang diamati satu instance breaker dan policy yang dikonfigurasi. Process yang berbeda karena itu dapat membuka dan pulih pada waktu berbeda.

Scope tersebut perlu disengaja. Breaker yang dipakai bersama oleh endpoint, tenant, atau kelas operasi yang tidak berkaitan dapat membuat satu jalur gagal memblokir pekerjaan yang sehat. Sebaliknya, breaker per request key dapat memecah observasi sampai tidak ada breaker yang menerima cukup sample untuk bereaksi. Boundary yang berguna biasanya mengikuti dependency atau operasi yang kegagalannya diperkirakan saling berkorelasi.

State half-open membatasi probe pemulihan

Setelah interval open, langsung melepaskan seluruh traffic baru atau yang menunggu dapat menciptakan kembali overload yang membuat dependency gagal. State half-open hanya menerima sejumlah kecil trial call.

open
  |
  | interval selesai
  v
half-open ---> probe terbatas ---> dependency
  |                               |
  | policy sukses terpenuhi       | gagal
  v                               v
closed                           open

Jika probe memenuhi recovery policy, breaker kembali closed dan admission normal berlanjut. Kegagalan yang memenuhi kriteria dapat mengembalikannya ke open. Implementasi berbeda dalam jumlah probe dan transition rule, sehingga detail tersebut sebaiknya diperlakukan sebagai policy, bukan semantics protocol yang universal.

Concurrency probe penting. Jika ratusan caller secara independen melihat interval telah selesai lalu semuanya menjadi probe, half-open tidak lagi membatasi load. State transition memerlukan koordinasi atomic dalam scope instance breaker agar hanya probe budget yang diizinkan dapat lewat.

Breaker dan retry menangani masalah berbeda

Retry menangani kemungkinan bahwa attempt berikutnya dapat berhasil. Circuit breaker memutuskan apakah sebuah attempt boleh mencapai dependency. Menggabungkan keduanya tanpa urutan yang jelas dapat melipatgandakan traffic.

Bayangkan tiga application attempt, masing-masing berisi tiga transport retry. Satu operasi logis dapat menghasilkan hingga sembilan remote call sebelum layer yang lebih tinggi menambahkan recovery behavior sendiri. Jika breaker menghitung setiap inner attempt, breaker melihat failure stream yang berbeda dibanding breaker yang membungkus seluruh retry operation.

Desain yang baik menyatakan komposisinya secara eksplisit:

request
  |
  v
retry policy
  |
  v
circuit breaker
  |
  v
dependency

atau, jika semantics yang diinginkan membutuhkannya:

request
  |
  v
circuit breaker
  |
  v
retry policy
  |
  v
dependency

Kedua susunan ini tidak setara. Susunan pertama membuat setiap retry attempt memeriksa state breaker. Susunan kedua memperlakukan retry sequence sebagai operasi yang diamati breaker. Penempatan timeout, attempt accounting, dan metrics juga berubah mengikuti komposisi tersebut.

Retry tetap harus terbatas dan sesuai dengan jenis operasi. Breaker tidak membuat mutation non-idempotent aman untuk diulang.

Timeout policy tetap diperlukan

Breaker baru dapat mencatat hasil setelah sebuah attempt menghasilkan outcome. Tanpa connection deadline dan request deadline yang terbatas, banyak panggilan dapat tetap in-flight sementara breaker memiliki terlalu sedikit bukti yang selesai untuk mengubah state.

Timeout karena itu tetap menjadi kontrol terpisah. Timeout membatasi berapa lama satu attempt dapat memakai resource. Breaker memakai outcome yang selesai untuk memutuskan apakah attempt berikutnya boleh diterima. Concurrency limit dapat menambah boundary lain dengan membatasi jumlah pekerjaan yang sudah in-flight.

Kontrol tersebut menangani dimensi berbeda:

timeout            -> membatasi durasi attempt
concurrency limit  -> membatasi pekerjaan simultan yang diterima
circuit breaker    -> menekan panggilan setelah outcome buruk
retry              -> mengizinkan attempt ulang tertentu

Memperlakukan semuanya sebagai policy terpisah membuat interaksinya terlihat dan mengurangi amplification yang tidak disengaja.

Fallback memerlukan model kapasitas sendiri

Breaker yang open sering mengalihkan execution ke fallback: cached data, degraded response, secondary service, atau error eksplisit. Fallback tidak otomatis lebih aman daripada jalur primary.

Jika setiap panggilan primary yang gagal dialihkan ke secondary dependency yang sama, secondary dapat menjadi bottleneck berikutnya. Response cache yang stale mungkin dapat diterima untuk satu endpoint dan tidak valid untuk endpoint lain. Default value dapat menyamarkan data yang hilang jika caller tidak dapat membedakannya dari hasil nyata.

Fallback behavior karena itu memerlukan contract eksplisit dan asumsi kapasitas. Pada sebagian jalur, mengembalikan kegagalan yang cepat dan terlihat lebih tepat daripada membuat degraded success.

Metrics perlu menampilkan state dan penyebab

State breaker saja tidak cukup untuk operasi. Telemetry yang berguna mencakup state transition, jumlah panggilan yang ditolak, jumlah probe yang diterima, kelas kegagalan yang diamati, slow-call count jika relevan, serta dependency latency sebelum breaker terbuka.

Transition perlu membawa identitas breaker dan scope policy. Alert yang hanya menyatakan circuit open jauh lebih sulit ditindaklanjuti dibanding alert yang terikat pada upstream, kelas operasi, dan kategori kegagalan tertentu.

State juga dapat berulang kali berubah ketika threshold terlalu dekat dengan variasi normal. Rolling window, minimum sample count, open interval, dan recovery criteria sebaiknya dipilih dari karakteristik traffic dan kegagalan service, bukan disalin sebagai konstanta generik.

Breaker adalah boundary traffic lokal

Circuit breaker paling berguna ketika kegagalan memiliki correlation boundary yang bermakna dan remote attempt membawa biaya nyata. Breaker mengubah bukti berulang tentang masalah dependency menjadi admission control lokal sementara, lalu memulihkan traffic melalui jalur terbatas.

Guarantee-nya tetap sederhana. Breaker yang open tidak menetapkan kesehatan global service yang sebenarnya, dan breaker yang closed tidak menjamin panggilan berikutnya berhasil. Nilainya berasal dari pembatasan remote work berulang dan admission pemulihan yang eksplisit, alih-alih membiarkan setiap caller menguji dependency yang gagal pada rate penuh.