Request Coalescing Meredam Lonjakan Cache Miss
Cache miss biasanya murah ketika satu caller memicu satu backend read. Miss yang sama dapat menjadi mahal ketika ratusan caller datang untuk key yang sama sebelum fill pertama selesai. Setiap caller melihat cache kosong lalu memulai work yang setara, sehingga load berlipat tepat ketika cached value sedang tidak tersedia.
Request coalescing menempatkan concurrency boundary kecil di sekitar fill tersebut. Caller pertama memulai operasi backend. Caller berikutnya untuk key yang sama bergabung dengan operasi yang sedang berjalan, bukan memulai operasi baru. Setelah selesai, hasilnya dapat mengisi cache sekaligus dikirim ke caller yang menunggu.
tanpa coalescing
A -- miss --> backend
B -- miss --> backend
C -- miss --> backend
dengan coalescing
A -- miss --+
B -- miss --+--> one fill --> backend
C -- miss --+Mekanisme ini sering disebut single-flight suppression. Scope yang tepat cukup sempit: work concurrent yang duplikat untuk logical key yang sama.
In-flight table terpisah dari cache
Cache menyimpan value yang sudah selesai dibuat. Coalescer melacak work yang sudah dimulai tetapi belum selesai. Menyatukan kedua peran itu secara konseptual dapat membuat aturan lifecycle menjadi rancu.
Implementasi sederhana menyimpan in-flight map dengan key yang sama seperti identitas fill:
inflight[key] = promise for current fillSaat terjadi miss, caller memeriksa map. Jika entry sudah ada, caller menunggu operasi tersebut. Jika belum ada, caller memasang entry baru dan bertanggung jawab menjalankan fill. Setelah operasi selesai, entry in-flight dihapus, baik hasilnya sukses maupun gagal.
Penghapusan ini penting. Promise gagal yang tertinggal di map dapat mengubah backend error sementara menjadi kegagalan lokal yang menetap. Sebaliknya, menghapus entry sebelum operasi selesai dapat membuka kembali celah duplikasi.
Map juga memerlukan check-and-install yang atomic. Dua caller yang sama-sama melihat entry belum ada sebelum salah satunya memublikasikan operasi masih dapat memulai dua fill. Mutex, synchronization primitive per key, atau fasilitas single-flight dari library dapat menyediakan serialisasi yang diperlukan.
Identitas key menentukan work yang boleh dibagi
Coalescing aman hanya ketika caller yang dikelompokkan pada satu key memang meminta work yang setara.
URL saja mungkin tidak cukup jika response juga bergantung pada tenant, authorization scope, locale, feature flag, query parameter, atau version selector. Jika input tersebut memengaruhi hasil backend, input itu harus masuk ke identitas coalescing kecuali layer lain sudah menormalisasikannya menjadi request yang setara.
bad key:
/profile
possible key:
tenant + user_id + representation_versionIni sekelas dengan persoalan desain cache key, tetapi konsekuensinya dapat muncul sebelum data masuk cache: satu caller bisa menerima hasil yang awalnya dijalankan untuk caller lain.
Desain yang lebih aman menurunkan cache lookup dan identitas in-flight dari canonical key function yang sama. Dengan begitu, equivalence pada cache dan equivalence pada coalescing lebih kecil kemungkinannya bergeser sendiri-sendiri.
Cancellation memerlukan semantics per caller
Beberapa caller dapat berbagi satu operasi backend dengan deadline berbeda. Membatalkan operasi bersama ketika satu waiter pergi biasanya terlalu agresif. Caller dengan deadline pendek dapat menghentikan work yang masih berguna bagi semua waiter lain.
Salah satu policy yang umum adalah membiarkan setiap waiter berhenti menunggu secara independen, sementara fill tetap berjalan selama masih berguna. Implementasi yang lebih kompleks dapat menghitung active waiter dan membatalkan operasi backend setelah waiter terakhir pergi.
Tidak ada satu policy yang cocok untuk semua kondisi. Operasi backend yang murah mungkin tetap layak diselesaikan walau tidak ada waiter, terutama jika hasilnya akan mengisi cache. Operasi mahal dapat lebih tepat dibatalkan setelah tidak ada caller yang dapat memakai hasilnya.
Pemisahan pentingnya ada antara waiting lifetime milik caller dan lifetime milik shared fill. Berbagi eksekusi tidak berarti setiap caller harus berbagi deadline yang sama.
Error tidak semestinya menjadi cache entry jangka panjang tanpa sengaja
Jika backend fill gagal, semua waiter saat itu dapat menerima error yang sama. Itu konsekuensi alami dari satu attempt yang dibagi. Namun error tersebut tidak perlu tersedia bagi caller berikutnya selama periode yang tidak ditentukan.
Setelah fill gagal, menghapus entry in-flight memberi kesempatan request berikutnya membuat attempt baru. Sistem yang sengaja menyimpan negative result atau error di cache memerlukan policy eksplisit dengan TTL dan klasifikasi error tersendiri.
Attempt baru yang langsung terjadi tetap dapat memberi pressure saat backend outage. Coalescing membatasi concurrency per key selama sebuah attempt aktif, tetapi tidak membatasi rate dari rangkaian attempt gagal yang terjadi berurutan. Retry backoff, admission control, circuit breaking, atau negative caching singkat dapat diperlukan untuk persoalan terpisah itu.
Coalescing mengubah distribusi latency follower
Caller pertama menanggung seluruh fill latency. Follower yang datang selama interval tersebut menunggu completion yang sama, sehingga observed latency bergantung pada saat mereka bergabung.
Follower yang datang mendekati akhir fill dapat selesai cepat. Follower yang datang sesaat setelah fill dimulai dapat menunggu hampir selama leader. Jadi, coalescing mengurangi backend work yang duplikat; mekanisme ini tidak membuat fill yang lambat menjadi cepat.
Metric sebaiknya membedakan leader dan follower:
fill_started_total
coalesced_waiter_total
inflight_keys
waiters_per_key
fill_duration
follower_wait_duration
fill_error_totalJumlah waiter yang tinggi pada satu key dapat memperlihatkan hot-key event walau volume request ke backend tampak kecil karena suppression bekerja.
Hot key tetap dapat memakai resource lokal
Mengubah 10.000 backend call menjadi satu merupakan pengurangan load backend yang besar, tetapi 10.000 waiter lokal tetap ada. Mereka memakai task, future, request state, memori, socket, atau resource runtime lain.
Karena itu, coalescing melengkapi bounded admission, bukan menggantikannya. Service dapat membatasi total concurrent request, jumlah waiter per key, atau menolak follower yang deadline-nya terlalu pendek untuk bergabung dengan fill yang sudah berjalan.
In-flight map juga memerlukan lifecycle yang bounded. Key harus hilang setelah operasi selesai, dan fill perlu deadline atau termination policy lain agar backend call yang tidak pernah selesai tidak menahan entry tanpa batas.
Cache expiry dapat dilunakkan sebelum stampede terbentuk
Coalescing paling terlihat setelah value tidak tersedia, tetapi cache policy dapat mengecilkan burst yang mencapainya.
TTL jitter mencegah banyak key yang tidak berkaitan kedaluwarsa pada saat yang sama. Refresh-ahead dapat memperbarui value populer sebelum hard expiry. Stale-while-revalidate dapat menyajikan stale value yang masih dapat diterima sementara satu caller melakukan refresh di background.
Setiap policy memiliki freshness semantics berbeda. Coalescing tidak otomatis memberi izin untuk menyajikan stale data, dan refresh-ahead tidak membuat setiap value aman dipakai kembali. Masing-masing teknik mengendalikan bagian berbeda dari tradeoff load dan freshness.
Walau policy tersebut digunakan, per-key in-flight guard tetap berguna ketika fill masih dapat dipicu secara concurrent setelah eviction, invalidation, process restart, atau cold deployment.
Cache tetap menjadi optimization boundary
Request coalescing tidak tepat dipakai untuk menciptakan correctness yang tidak disediakan backend. Jika dua write harus diserialkan, atau sebuah read wajib mengamati transaction order tertentu, cache-fill coalescer bukan coordination primitive yang tepat.
Kontraknya lebih sederhana: caller yang memenuhi syarat untuk melakukan read-like work yang setara pada waktu bersamaan dapat berbagi satu eksekusi. Authoritative system tetap menentukan value beserta consistency semantics-nya.
Kontrak yang sempit membuat mekanisme ini praktis. Cache miss tidak lagi menjadi pemicu bagi setiap caller concurrent untuk mengulang operasi mahal yang sama. Satu fill berjalan, follower yang kompatibel menunggunya, lalu in-flight state hilang setelah attempt berakhir.