Request Coalescing Mencegah Cache Miss Melipatgandakan Beban Backend

Cache miss biasanya murah ketika satu caller memicu satu lookup ke backend. Miss yang sama dapat menjadi mahal ketika banyak caller datang untuk key yang sama dalam waktu hampir bersamaan. Setiap caller melihat key belum tersedia, masing-masing memulai pekerjaan identik, lalu backend menerima lonjakan justru ketika cache tidak memberi perlindungan untuk key tersebut.

Request coalescing mengubah pola concurrency itu. Caller pertama menjadi leader untuk sebuah key. Caller berikutnya bergabung dengan operasi yang sedang berjalan dan menunggu hasilnya, bukan memulai pekerjaan setara. Setelah fill selesai, hasilnya dapat mengisi cache sekaligus dikembalikan kepada caller yang menunggu.

Satu cache miss dapat memperbesar beban

Misalkan sebuah entry populer kedaluwarsa ketika 200 request untuk key tersebut datang dalam interval singkat. Tanpa koordinasi, seluruh 200 request dapat mengakses database atau layanan remote.

tanpa coalescing

request A --miss--> backend
request B --miss--> backend
request C --miss--> backend
request D --miss--> backend

Satu entry yang kedaluwarsa berubah menjadi banyak fill concurrent. Pola ini sering disebut cache stampede. Dampaknya dapat menaikkan concurrency di backend, menghabiskan connection pool, dan menambah latency bagi traffic lain yang memakai dependency yang sama.

Dimensi pentingnya bukan hanya total cache miss rate. Miss rate yang sedang tetapi terkonsentrasi pada satu hot key dapat menciptakan concurrency yang lebih merusak daripada miss rate lebih tinggi yang tersebar pada banyak key independen.

Coalescing adalah sinkronisasi berbasis key

Coalescer mencatat pekerjaan yang sudah berjalan untuk sebuah key. Caller baru memeriksa registry in-flight tersebut sebelum memulai fill.

request A --miss--+--> fill(key) --> backend
request B --miss--|
request C --miss--+--> tunggu fill yang sama
request D --miss--|

State machine minimal hanya memerlukan beberapa transisi:

absent -> in-flight -> completed -> absent
                    \-> failed  -> absent

Value yang selesai biasanya berada di cache, bukan di registry in-flight. Registry mengoordinasikan pekerjaan concurrent; cache mengatur penggunaan ulang setelah pekerjaan selesai.

Registrasi harus bersifat atomic. Jika dua caller sama-sama melihat tidak ada entry in-flight lalu keduanya memasang leader, properti utama mekanisme ini sudah hilang. Mutex, primitive concurrent map, actor, atau titik serialisasi setara dapat melindungi transisi tersebut.

Coalescing tidak membuat satu hasil dibagi selamanya

Follower berbagi satu eksekusi tertentu, bukan data dengan masa berlaku tanpa batas. Setelah leader selesai dan entry in-flight dihapus, cache miss berikutnya dapat memulai fill baru.

Pemisahan ini menjaga freshness policy tetap terpisah dari concurrency control. TTL, invalidation eksplisit, version check, dan aturan stale tetap menentukan apakah cached value boleh dipakai kembali. Coalescing hanya menentukan apakah caller serentak perlu menduplikasi fill yang sama.

Karena itu, key untuk coalescing harus mewakili operasi secara akurat. Jika authorization scope, locale, query parameter, tenant, representation format, atau input lain mengubah hasil, input tersebut mungkin perlu menjadi bagian dari key. Menggabungkan request yang secara semantik berbeda dapat mengirim data kepada caller yang salah.

Kegagalan memerlukan kebijakan fan-out yang eksplisit

Jika leader gagal, setiap follower yang menunggu eksekusi tersebut memerlukan hasil yang terdefinisi. Kebijakan paling sederhana meneruskan error yang sama kepada seluruh waiter lalu menghapus entry in-flight agar request berikutnya dapat mencoba lagi.

Retry independen secara langsung dari setiap follower merusak koordinasi. Fill yang gagal lalu diikuti ratusan retry serentak hanya memindahkan stampede ke jalur kegagalan.

Layanan dapat menggabungkan coalescing dengan retry terbatas, backoff, jitter, stale data, atau negative cache singkat jika sesuai dengan kontrak data. Mekanisme tersebut menangani bagian masalah yang berbeda: coalescing membatasi pekerjaan concurrent yang duplikat, sedangkan kebijakan retry dan caching mengatur percobaan berikutnya.

Cancellation caller dan pekerjaan bersama adalah keputusan terpisah

Satu follower dapat kehilangan minat sebelum shared fill selesai. Koneksi HTTP-nya mungkin tertutup atau deadline-nya habis. Membatalkan operasi dasar secara langsung dapat keliru karena caller lain masih bergantung padanya.

Coalescer membutuhkan kebijakan cancellation. Pilihan umum mencakup mempertahankan leader sampai deadline miliknya sendiri, menghitung waiter aktif lalu membatalkan hanya ketika tidak ada yang tersisa, atau memakai fill context yang independen dari context tiap caller.

caller A cancelled ----X
caller B waiting -------+--> shared fill berlanjut
caller C waiting -------+

Kebijakan yang tepat bergantung pada biaya dan semantik operasi backend. Satu lifecycle caller tidak boleh secara tidak sengaja mengendalikan caller lain hanya karena mereka berbagi fill yang sama.

Leader yang lambat dapat menahan banyak follower

Coalescing mengurangi pekerjaan backend, tetapi juga menciptakan ketergantungan sementara pada satu eksekusi. Jika eksekusi itu macet, seluruh follower untuk key tersebut ikut menunggu.

Fill membutuhkan deadline yang terbatas. Timeout backend tetap berlaku, dan telemetry sebaiknya menampilkan durasi leader serta waktu tunggu follower. Jumlah waiter yang besar pada fill lambat merupakan sinyal saturasi yang berguna meski jumlah request ke backend tetap rendah.

Sebagian sistem mengizinkan fill kedua setelah threshold tertentu agar semua caller tidak terus tertahan di belakang satu leader yang sangat lambat. Ini merupakan trade-off yang disengaja: sedikit pekerjaan duplikat dapat menurunkan tail latency, tetapi threshold yang terlalu agresif menciptakan kembali amplifikasi awal. Kebijakan seperti ini memerlukan batas concurrency per key yang ketat.

Koordinasi process-local memiliki batas process-local

Coalescer in-memory hanya menggabungkan request yang mencapai process yang sama. Dengan 20 instance aplikasi, satu hot miss masih dapat menghasilkan hingga 20 fill backend walaupun setiap instance melakukan local coalescing dengan sempurna.

Kondisi itu sering kali dapat diterima. Mengurangi ribuan fill menjadi puluhan dapat menghilangkan sebagian besar tekanan tanpa menambahkan distributed coordination ke request path.

Coalescing lintas process memerlukan mekanisme koordinasi lain, seperti shared lock atau fitur cache dengan operasi atomic yang sesuai. Pilihan itu menambah failure mode terkait ownership, expiry, network partition, dan stale holder. Distributed lock tidak perlu ditambahkan hanya demi satu fill secara teoretis jika sejumlah fill per instance yang terbatas masih aman secara operasional.

Refresh cache dapat menghindari miss path

Coalescing sangat berguna untuk miss yang sulit diprediksi, tetapi hot entry kadang dapat di-refresh sebelum expiry. Refresh-ahead memulai pekerjaan baru ketika value lama masih dapat digunakan sehingga peluang caller menemui cache kosong pada saat yang sama berkurang.

Stale-while-revalidate memakai pola terkait: satu request memperbarui entry yang kedaluwarsa atau menua, sementara caller lain sementara menerima stale value yang masih diizinkan. Pola ini dapat menghilangkan waktu tunggu follower sekaligus pekerjaan backend duplikat, selama kontrak produk mengizinkan stale data.

Kebijakan tersebut tetap membutuhkan batas. Me-refresh setiap key yang jarang dipakai membuang kapasitas, sedangkan menyajikan stale value tanpa batas dapat menyembunyikan kegagalan backend. Popularity threshold, maximum stale age, dan refresh deadline menjaga perilakunya tetap terbatas.

Metric perlu menampilkan suppression selain miss

Cache hit ratio yang tinggi masih dapat berdampingan dengan stampede berat jika miss yang tersisa terkonsentrasi pada sedikit key. Telemetry yang berguna mencakup jumlah fill dimulai, follower bergabung, jumlah waiter, durasi fill, waktu tunggu follower, kegagalan fill, serta rasio fill yang ditekan terhadap fill backend aktual.

Raw cache key dapat memuat nilai sensitif atau high-cardinality, sehingga metric sebaiknya tidak memakai key arbitrer sebagai label. Agregasi berdasarkan route, cache namespace, operation, atau kelas key yang terbatas biasanya lebih aman. Diagnosis key secara detail dapat ditempatkan pada sampled trace atau log terkontrol jika sesuai.

Coalescer yang hanya melaporkan backend call menyembunyikan jumlah demand yang diserapnya. Suppression metric menunjukkan apakah mekanisme benar-benar mengurangi pekerjaan duplikat atau hanya menambahkan sinkronisasi pada traffic yang memang sudah independen.

Satu fill yang berjalan dapat menyerap lonjakan

Cache mengurangi pekerjaan berulang sepanjang waktu. Request coalescing mengurangi pekerjaan berulang yang terjadi secara concurrent. Perbedaannya penting ketika key populer hilang, kedaluwarsa, atau masih cold pada process yang baru dimulai.

Implementasi yang kuat memilih satu leader per key secara atomic, memberi follower waktu tunggu terbatas, memisahkan cancellation caller dari shared execution, menghapus state in-flight yang gagal atau selesai, dan menjaga coalescing key tetap selaras dengan semantik hasil. Distributed coordination bersifat opsional; suppression process-local saja sudah dapat mengurangi pekerjaan duplikat secara besar.

Mekanisme ini tidak menambah kapasitas backend. Ia mencegah caller serentak menghabiskan kapasitas tersebut untuk fill yang sama ketika satu eksekusi dapat melayani semuanya.