Request Collapsing atau yang populer disebut pola Singleflight adalah teknik optimasi backend untuk menggabungkan beberapa permintaan (request) identik yang masuk secara bersamaan ke dalam satu eksekusi tunggal. Ketika ratusan atau ribuan klien meminta data yang sama persis dalam milidetik yang sama, server hanya mengeksekusi satu proses fetch ke database atau layanan hulu (upstream), lalu mendistribusikan hasil tunggal tersebut ke semua peminta yang menunggu. Teknik ini menjadi pertahanan utama terhadap masalah Thundering Herd Problem dan Cache Stampede.
Tanpa mekanisme collapsing, lonjakan trafik mendadak pada data yang belum masuk cache (cache miss) akan membuat database kewalahan karena memproses kueri yang sepenuhnya identik secara berulang-ulang secara paralel. Dengan menerapkan singleflight, latensi sistem terjaga, penggunaan CPU dan memori terkontrol, serta mencegah kegagalan total pada infrastruktur backend.
Mekanisme Kerja Singleflight
Secara arsitektural, pola singleflight bekerja menggunakan struktur data peta (map) yang dikunci (mutex) untuk melacak permintaan yang sedang aktif berjalan. Alur kerjanya dapat dijabarkan melalui tahapan teknis berikut:
- Penerimaan Request: Klien A, B, dan C secara bersamaan meminta data dengan kunci unik (misal:
product:105). - Pemeriksaan Status: Server memeriksa apakah kunci
product:105sedang diproses dalam memori. - Penggabungan (Collapse): Karena Klien A datang pertama, proses pengambilan data ke database dijalankan. Klien B dan C yang datang sepersekian milidetik kemudian tidak menjalankan kueri baru, melainkan digabungkan (attached) ke channel atau promise dari proses Klien A.
- Distribusi Hasil: Ketika data dari database berhasil diterima oleh proses Klien A, hasil tersebut disalin dan dikirimkan secara serentak ke Klien A, B, dan C.
- Pembersihan Kunci: Setelah selesai, kunci dihapus dari map agar permintaan berikutnya di masa depan dapat memicu proses baru jika diperlukan.
Matriks Perbandingan: Tanpa Singleflight vs Menggunakan Singleflight
| Parameter | Tanpa Request Collapsing | Dengan Request Collapsing (Singleflight) |
|---|---|---|
| Beban Database | Tinggi (1000 request = 1000 kueri database) | Minimal (1000 request = 1 kueri database) |
| Penggunaan CPU/RAM | Lonjakan drastis saat cache miss | Stabil dan terkontrol |
| Risiko Thundering Herd | Sangat Tinggi | Sangat Rendah / Tereliminasi |
| Latensi Rata-rata | Meningkat tajam akibat antrean database | Rendah, karena klien lain langsung mendapat hasil dari memori |
Contoh Implementasi Golang (Package golang.org/x/sync/singleflight)
Bahasa pemrograman Go menyediakan implementasi resmi melalui pustaka standar eksternal yang sangat stabil dan banyak digunakan di lingkungan produksi industri:
package main
import (
"context"
"fmt"
"sync"
"time"
"golang.org/x/sync/singleflight"
)
var g singleflight.Group
func slowDBQuery(ctx context.Context, key string) (string, error) {
// Simulasi latensi database
time.Sleep(500 * time.Millisecond)
return "Data hasil dari database untuk: " + key, nil
}
func GetData(ctx context.Context, key string) (string, error) {
// Do melahirkan eksekusi tunggal untuk key yang sama
val, err, shared := g.Do(key, func() (interface{}, error) {
return slowDBQuery(ctx, key)
})
if err != nil {
return "", err
}
fmt.Printf("Apakah data dibagikan (shared)? %v\n", shared)
return val.(string), nil
}
func main() {
var wg sync.WaitGroup
ctx := context.Background()
// Mensimulasikan 5 request bersamaan untuk key yang sama
for i := 0; i < 5; i++ {
wg.Add(1)
go func(id int) {
defer wg.Done()
result, _ := GetData(ctx, "user_settings_99")
fmt.Printf("Goroutine %d mendapat: %s\n", id, result)
}(i)
}
wg.Wait()
}
Penerapan pada Bahasa Lain
Meskipun istilah Singleflight dipopulerkan oleh ekosistem Go, konsep serupa dapat diterapkan di berbagai platform teknologi lain:
- Node.js / JavaScript: Menggunakan pola
Promise Memoizationatau pustaka pihak ketiga sepertisingle-flightuntuk mencegah pemanggilan fungsi asinkron ganda. - Python (Asyncio): Mengelola kamus tugas aktif (active tasks dictionary) di mana koroutin berikutnya melakukan
awaitpada objek Future/Task yang sama jika kunci sedang dikerjakan. - Java: Memanfaatkan
CompletableFutureyang dikombinasikan denganConcurrentHashMapuntuk memastikan eksekusi komputasi berat hanya berjalan sekali per kunci aktif.
Troubleshooting dan Batasan Lapangan
Meskipun sangat efektif, pengembang harus memperhatikan beberapa catatan penting saat menerapkan pola ini di sistem skala besar:
- Distribusi Multi-Server (Distributed Lock): Singleflight standar hanya bekerja di dalam memori satu instance server (in-process). Jika Anda menggunakan arsitektur load balancer dengan banyak pod/server, singleflight lokal tidak akan mencegah duplikasi request antar server yang berbeda. Untuk skala cluster, diperlukan Redis Distributed Lock atau Redlock.
- Penanganan Timeout: Pastikan konteks pembatalan (context cancellation) diatur dengan benar agar jika request utama gagal atau timeout, seluruh klien yang ikut mengantre tidak mengalami kegagalan senyap.
- Stale Data: Karena hasil dibagikan ke banyak klien dalam satu jendela waktu, pastikan data tersebut tidak bersifat sangat unik per detik jika memerlukan personalisasi tinggi.