Cache
Dalam pengembangan aplikasi modern, caching adalah salah satu strategi paling efektif untuk meningkatkan performa dan mengurangi beban database. Buku ini akan memandu Anda melalui implementasi caching yang robust menggunakan Valkey (fork dari Redis).
📂 Kode Lengkap Bab Ini: Seluruh kode yang dibahas di bab ini tersedia di GitHub:
22.1 Masalah yang Dihadapi
Bayangkan Anda memiliki aplikasi dengan fitur daftar pengguna (List Users) yang dipanggil ribuan kali per detik. Setiap request akan:
Query database (
SELECT * FROM users)Mengirimkan data ke user
Memproses query yang sama berulang kali
Dampak:
Database overload
Response time lambat (100ms+)
Biaya infrastruktur tinggi
22.2 Solusi: Caching
Dengan caching, kita menyimpan hasil query di memory (Valkey/Redis) sehingga:
Metrik
Tanpa Cache
Dengan Cache
Response Time
100ms+
1-5ms
Database Load
100%
< 10%
Skalabilitas
Terbatas
Sangat baik
22.3 Memilih Cache Server
Redis banyak digunakan sebagai server cache, namnun semenjak redis tidak lagi open source, komunitas opensource membuat valkey yang di-fork dari redis versi 7.2.
Mnejalankan Valkey dengan Docker
22.4 Memilih Library Go untuk Valkey
valkey-glide
Multi-language support (Rust core), AZ Affinity Routing (optimasi biaya cloud), Stable dan enterprise-ready, Consisten di semua bahasa
1 koneksi multiplex per node, Perlu client terpisah untuk operasi besar
valkey-go
Native Go, sangat cepat, Auto-pipelining, Client-side caching, Connection pool support
Hanya untuk Go, Fitur enterprise terbatas
Rekomendasi
Cloud (AWS/GCP) -> Gunakan
valkey-glide(AZ Affinity routing menghemat biaya)On premise -> Gunakan
valkey-go(Lebih ringan).
22.5 Arsitektur Caching
Prinsip Dasar
Operasi terkait cache dihandle oleh Service Layer, bukan Handler atau Repository:
Cache-Aside Pattern
Cache-Aside adalah strategi caching paling umum. Aplikasi bertanggung jawab penuh mengelola cache:
22.6 Implementasi Cache Client
Interface Cache Client
Implementasi Standalone Valkey
Konfigurasi Cache
22.7 Helper List Cache (Key Registering)
Untuk cache dengan banyak parameter (seperti List Users dengan sorting, filtering, pagination), kita perlu strategi khusus untuk mengelola key. Masalah
Pada List Users, setiap kombinasi parameter menghasilkan key berbeda:
users::list::order:name::sort:asc::search:admin::limit:10::page:1users::list::order:name::sort:desc::search:admin::limit:10::page:1users::list::order:email::sort:asc::search:john::limit:20::page:2
Masalah pada Write Flow: Bagaimana menghapus semua key yang relevan saat data berubah?
Pendekatan yang umum digunakan ada 3:
Cache dengan TTL pendek, sehingga tidak perlu dipusingkan bagaimana melakukan invalidate, karena cache secara otomatis expired ketika TTL habis. Kelemahannya adalah data cache tidak bisa dijamin merupakan data paling baru.
Versioning Number. Dibuat cache untuk mengelola versioning dari setiap kelompok cache. Keuntungannya data cache selalu paling update, mudah diterapkan, dan tidak perlu dipusingkan dengan invalidate cache. Kelemahanya storage membengkak karena cache yang sudah tidak valid masih disimpan hingga TTL nya habis.
Key Registering, dimana setiap key didaftarkan ke dalam index_list, sehingga key yang sudah tidak terpakai bisa dihapus. Keuntungan data cache tetap terbaru, storage tidak membengkak, namun kompleksitasnya tinggi.
Solusi: Key Registering dengan SET
Helper List Cache
Buat helper listcache dengan fungsi :
GenereateKey untuk konsistensi penamaan key cache
AddKeyToIndex untuk proses registering key
InvalidateListCache untuk proses invalidate cache
Cleanup untuk menghapus member key yang sudah expired
Buat file pkg/listcache/helper.go
22.8 Implementasi Cache di Service User
22.9 Mengatasi Masalah Cache
Cache Stampede
Masalah: Cache expired bersamaan → banyak request hit database simultaneously.
Solusi: Tambahkan jitter pada TTL:
Thundering Herd
Masalah: Banyak request konkuren untuk data yang sama → semua request database.
Solusi: Gunakan Single Flight Pattern (hanya 1 request ke database, sisanya menunggu):
Lebih detail terkait pattern single flight bisa dibaca di Single Flight Pattern.
Operasi Penghapusan Data yang Besar
Masalah: SMEMBERS + DEL untuk ribuan key bersifat blocking.
Solusi: Gunakan SSCAN untuk iterasi bertahap (sudah diimplementasikan di cleanupStaleIndexBatch).
Operasi Blocking di Valkey Glide
Masalah: Valkey Glide menggunakan multiplex yang unggul secara throughput namun lemah terhadap operasi blcoking seperti BLPOP atau penghapusan data yang besar.
Solusi:
On premise : gunakan valkey-go, dimana koneksi valkey otomatis dijalankan dengan multiplex untuk perintah nonblocking, namun perintah blocking akan dijalankan dengan koneksi pool connection.
Cloud AWS/GCP : buat dua koneksi client. Pilihan koneksi diset secara manual di dalam kode agar operasi blocking tidak menghambat operasi nonblocking. Jika operasi nonblocking gunakan koneksi A, semantara operasi blocking menggunakan koneksi B.
22.10 Update Bootstrap dengan Cache
Ringkasan Bab 22
Di bab ini kita telah belajar:
Cache Client Interface
Abstraksi untuk berbagai implementasi cache
Valkey Standalone
Implementasi dengan valkey-glide
Cache-Aside Pattern
Strategi caching dengan invalidation manual
Key Registering
Mengelola cache dengan banyak parameter menggunakan SET
List Cache Helper
Generate key, add to index, invalidate, cleanup
Single Flight
Mencegah Thundering Herd
Manfaat yang kita peroleh:
✅ Response time turun dari 100ms ke 1-5ms
✅ Beban database berkurang 90%+
✅ Cache terkelola dengan baik (invalidation otomatis)
✅ Index management dengan SET (SADD, SMEMBERS, SSCAN, SREM)
✅ Proteksi dari Cache Stampede dan Thundering Herd
Yang akan datang:
Saat ini kita belum memanggil API eksternal
Bab selanjutnya: Call API Third Party – memanggil service eksternal dengan retry, timeout, dan circuit breaker
Last updated