Sebuah service dapat tetap sehat pada level proses tetapi menjadi tidak berguna karena satu workload menghabiskan seluruh resource eksekusi yang langka. Dependency yang lambat dapat menahan semua outbound connection. Tenant yang sangat aktif dapat memenuhi setiap worker slot. Background job dapat mengambil semaphore permit yang sama dengan request interaktif.
Isolasi bulkhead membatasi keterkaitan tersebut. Alih-alih membiarkan pekerjaan yang tidak berkaitan berebut satu pool tanpa pembagian, sistem mempartisi resource tertentu dan memberi setiap kelas pekerjaan bagian yang terbatas. Saturasi kemudian memiliki blast radius yang lebih kecil.
Shared pool menghubungkan mode kegagalan yang berbeda
Pooling efisien ketika workload memiliki perilaku serupa. Kapasitas idle dapat dipakai kembali, lebih sedikit resource yang menganggur sebagai cadangan, dan utilisasi cenderung membaik.
Pembagian yang sama juga menciptakan jalur kegagalan. Misalkan dua kelas request memakai worker pool berisi 100 slot. Kelas A biasanya memakai 20 slot, sedangkan kelas B memakai 30. Jika B mulai memanggil dependency yang macet selama beberapa detik, request B yang masih in-flight pada akhirnya dapat menempati seluruh 100 slot. Kelas A kemudian terhambat meskipun dependency dan code path miliknya sehat.
Tidak ada komponen A yang gagal secara langsung. Shared pool meneruskan saturasi B ke A.
Connection, thread, async concurrency permit, file descriptor, memory budget, queue slot, dan rate budget dapat menjadi titik keterkaitan seperti ini. Resource tersebut tidak harus berupa thread pool secara literal.
Bulkhead menetapkan failure domain yang terbatas
Bulkhead membagi shared resource berdasarkan batas yang memiliki arti operasional. Batasnya dapat berupa dependency, kelas tenant, kelompok endpoint, tingkat prioritas, atau tipe workload.
Jika A memperoleh 40 concurrency permit dan B memperoleh 60, B tidak dapat memakai 40 permit milik A. Saat B mencapai batasnya, pekerjaan B berikutnya harus menunggu, gagal lebih awal, atau mengikuti kebijakan overload eksplisit lainnya. A tetap memiliki kapasitas yang dialokasikan.
Partisi tersebut tidak membuat B kembali sehat. Partisi mempertahankan kapasitas di luar failure domain B.
Perbedaan ini penting. Bulkhead adalah mekanisme containment, bukan mekanisme recovery. Timeout, cancellation, circuit breaker, retry, dan perbaikan dependency tetap menangani bagian kegagalan yang lain.
Isolasi dapat diterapkan pada beberapa lapisan resource
Sebuah service tidak memerlukan proses terpisah untuk setiap workload. Isolasi yang berguna sering dimulai dari resource sempit yang membawa contention.
Semaphore per dependency dapat membatasi jumlah call konkuren ke setiap remote service. Connection pool terpisah dapat mencegah satu database atau upstream endpoint memakai seluruh connection. Worker queue yang berbeda dapat mencegah batch work mengambil semua interactive worker. Concurrency limit per tenant dapat menghentikan satu customer menghabiskan shared executor.
Batas yang lebih kuat dapat memakai proses, container, atau service instance terpisah ketika CPU, memory, crash containment, atau deployment independence membutuhkannya.
Batas isolasi harus sesuai dengan resource yang berisiko. Memisahkan queue sementara semua pekerjaan tetap bergantung pada satu connection pool yang sudah habis memberi perlindungan kecil. Connection pool terpisah juga tidak mengisolasi saturasi CPU jika kedua workload masih dapat memakai seluruh runnable worker.
Reserved capacity memiliki biaya utilisasi
Isolasi tidak gratis. Partisi yang kaku dapat membuat kapasitas menganggur di satu pool sementara pool lain menolak pekerjaan yang sebenarnya berguna.
Bayangkan 100 worker slot dibagi rata antara A dan B. Jika A membutuhkan 10 dan B membutuhkan 70, pembagian tetap 50/50 menyisakan 40 slot A tanpa pekerjaan sementara B dibatasi pada 50. Pool yang sepenuhnya dibagi dapat melayani beban tersebut.
Trade-off ini menjadi inti sizing. Tujuannya bukan pemisahan maksimum dengan biaya berapa pun. Sistem memerlukan isolasi yang cukup untuk mempertahankan layanan kritis saat kondisi overload yang masuk akal terjadi, tanpa membuang terlalu banyak kapasitas selama traffic normal.
Sebagian sistem menggabungkan reserved capacity dan shared capacity. Setiap kelas memperoleh minimum yang terlindungi, sementara kapasitas tersisa dapat dipinjam dengan aturan terkontrol. Peminjaman meningkatkan utilisasi, tetapi semantik reclaim harus jelas. Kapasitas yang tidak dapat diambil kembali saat saturasi bukan reserved capacity yang nyata.
Isolasi queue harus mencakup admission limit
Queue terpisah hanya berguna jika masing-masing memiliki batas dan kebijakan admission yang jelas. Unlimited queue pada satu partisi masih dapat menghabiskan process memory dan menghasilkan pekerjaan usang yang bertahan lama.
Bulkhead umumnya memasangkan concurrency limit dengan bounded queue kecil. Concurrency limit membatasi konsumsi resource aktif. Queue menyerap selisih singkat antara laju kedatangan dan penyelesaian. Setelah keduanya penuh, boundary menolak atau menunda pekerjaan baru sesuai kontrak service.
Panjang queue tidak seharusnya dianggap sebagai kapasitas tersembunyi. Request yang menunggu di queue tetap memakai waktu dari deadline dan dapat menahan memory atau state lain.
Untuk pekerjaan yang sensitif terhadap latency, queue pendek atau penolakan langsung dapat lebih aman daripada menerima request yang kemungkinan tidak mulai diproses sebelum deadline habis.
Timeout dan cancellation melepaskan kapasitas terisolasi
Bulkhead masih dapat terisi permanen jika pekerjaan di dalamnya tidak memiliki batas waktu penyelesaian. Semaphore dengan 20 permit memberi sedikit perlindungan jika 20 call dapat macet tanpa batas.
Timeout membatasi durasi operasi yang macet dalam menahan slot. Cancellation memungkinkan pekerjaan yang sudah kehilangan caller atau melewati deadline berhenti memakai kapasitas ketika operasi dasarnya mendukung interupsi yang aman.
Pelepasan permit harus terkait dengan penyelesaian operasi yang sebenarnya atau semantik cancellation. Melepas permit hanya karena caller berhenti menunggu dapat melanggar concurrency bound jika operasi downstream masih berjalan di background.
Detail ini sangat penting pada async API. Penyelesaian logis pada satu lapisan tidak selalu berarti konsumsi resource sudah berakhir pada lapisan berikutnya.
Retry harus tetap berada dalam capacity budget yang sama
Retry dapat melewati isolasi jika setiap attempt yang gagal membuat pekerjaan konkuren baru tanpa accounting terhadap budget awal.
Client yang melakukan retry harus memperoleh kapasitas bulkhead yang relevan untuk setiap attempt aktif. Backoff dapat mengurangi tekanan retry, tetapi tidak menggantikan concurrency bound. Hedged request memerlukan accounting serupa karena attempt yang overlap memang sengaja menambah pekerjaan konkuren.
Jika retry traffic memiliki pool terpisah, pemisahan tersebut harus disengaja. Jika tidak, insiden dependency dapat memindahkan load dari primary pool ke jalur retry tanpa batas dan menciptakan kembali exhaustion melalui rute lain.
Partition key memerlukan arti operasional yang stabil
Partisi yang terlalu sedikit membuat workload yang tidak berkaitan tetap saling terhubung. Terlalu banyak partisi menghasilkan pool kecil yang memecah kapasitas dan sulit diatur ukurannya.
Pool dinamis per request biasanya kurang cocok karena memperbanyak state dan menghilangkan sharing yang berguna. Kelas dengan arti operasional lebih mudah ditangani: traffic kritis versus best-effort, pekerjaan interaktif versus batch, atau remote dependency yang independen.
Isolasi tenant memerlukan perhatian tambahan ketika jumlah tenant besar. Pool tetap untuk setiap tenant dapat menjadi tidak praktis. Global limit yang digabung dengan cap per tenant, weighted scheduling, atau bounded active-tenant state sering menghasilkan struktur yang lebih mudah dikelola.
Partition key merupakan bagian dari reliability model. Key tersebut perlu mencerminkan workload mana yang boleh gagal bersama dan workload mana yang harus mempertahankan kapasitas secara independen.
Metrik perlu memperlihatkan tekanan per partisi
Utilisasi agregat dapat menyembunyikan bulkhead yang jenuh. Sebuah service dapat menunjukkan utilisasi worker keseluruhan 45 persen sementara satu partisi kritis berada pada 100 persen dan menolak traffic.
Ukur operasi aktif, configured limit, queue occupancy, queue wait, rejection count, timeout count, cancellation count, dan completion latency untuk setiap partisi. Label dependency dan tenant dapat berguna jika cardinality-nya dikendalikan.
Pantau juga reserved capacity yang tidak terpakai. Rejection yang terus terjadi pada satu partisi bersamaan dengan kapasitas idle yang menetap pada partisi lain merupakan sinyal bahwa ukuran partisi atau borrowing policy perlu ditinjau.
Metrik bulkhead paling berguna ketika menunjukkan kedua sisi trade-off: containment saat kegagalan dan fragmentasi saat operasi normal.
Isolasi perlu mempertahankan layanan yang penting
Bulkhead mengubah global resource pool menjadi failure domain yang eksplisit. Nilainya terlihat ketika satu workload melambat atau membanjiri sistem: pekerjaan yang dilindungi tetap memiliki worker, connection, permit, atau queue space.
Desain ini paling kuat ketika partition boundary sesuai dengan jalur contention nyata, setiap partisi memiliki batas, pekerjaan yang macet memiliki lifetime terbatas, dan retry tetap tunduk pada accounting yang sama. Dengan batasan tersebut, overload dapat menurunkan kualitas satu bagian service tanpa otomatis menghabiskan kapasitas eksekusi seluruh bagian lain.