Rate Limiting?
Apa Itu Rate Limiting?
Secara sederhana, Rate Limiting adalah sebuah mekanisme kontrol untuk membatasi seberapa sering seseorang (atau sebuah layanan) dapat melakukan suatu tindakan dalam periode waktu tertentu.
Tujuannya adalah untuk:
Stabilitas: Mencegah satu pengguna membebani server dan membuatnya down (misalnya, serangan DDoS).
Keamanan: Mencegah serangan brute force (misal, mencoba-coba password ribuan kali).
Keadilan (Fairness): Memastikan semua pengguna mendapatkan "jatah" sumber daya yang adil.
Biaya: Mengontrol biaya penggunaan API pihak ketiga.
Berikut adalah evolusi algoritma rate limiter, dari yang paling dasar hingga yang paling ideal.
1. Fixed Window Counter (Penghitung Jendela Tetap)
Ini adalah algoritma rate limiter yang paling sederhana dan paling dasar.
Penjelasan
Algoritma ini membagi waktu menjadi "jendela" (Windows) dengan durasi tetap (misal, 1 menit). Setiap jendela memiliki counter (penghitung) yang akan bertambah setiap kali ada request masuk.
Jika counter dalam jendela tersebut telah mencapai batas limit, semua request berikutnya dalam jendela itu akan ditolak. Ketika jendela waktu baru dimulai, counter akan di-reset kembali ke nol.
Ilustrasi / Analogi
Bayangkan sebuah loket bioskop yang memiliki aturan: "Hanya menjual 100 tiket per jam."
Dari jam 10:00:00 hingga 10:59:59, loket akan menghitung tiket yang terjual.
Jika 100 tiket sudah terjual pada jam 10:45:00, loket akan tutup.
Siapa pun yang datang antara 10:45:01 dan 10:59:59 akan ditolak.
Tepat pada jam 11:00:00, counter di-reset ke 0, dan loket mulai menjual 100 tiket lagi.
Flow Algoritma
Sebuah request masuk dari pengguna (misal, IP
1.2.3.4).Tentukan window waktu saat ini (misal,
10:01:00).Ambil counter untuk
1.2.3.4pada window10:01:00.Jika
counter<limit(misal, 100):Naikkan
counter(misal,counter++).Izinkan request.
Jika
counter>=limit:Tolak request.
Saat waktu memasuki window baru (misal,
10:02:00), counter untuk10:01:00dibuang atau di-reset.
Kelebihan
Sangat Sederhana: Mudah diimplementasikan dan dipahami.
Hemat Memori: Hanya perlu menyimpan satu counter untuk setiap pengguna per window.
Kekurangan & Problem
Problem Utama: Lonjakan di Tepi Jendela (Edge Burst): Ini adalah kelemahan fatalnya.
Misal, limit 100 request/menit.
Pengguna A mengirim 100 request pada jam 10:01:59. (Diizinkan)
Pada jam 10:02:00, counter di-reset.
Pengguna A mengirim lagi 100 request pada jam 10:02:01. (Diizinkan)
Hasilnya: Server menerima 200 request hanya dalam 2 detik! Ini jelas melanggar tujuan rate limiting dan bisa membuat server down.
2. Sliding Log (Log Geser)
Algoritma ini memperbaiki masalah burst pada Fixed Window dengan cara yang lebih presisi.
Penjelasan
Alih-alih menghitung jumlah, algoritma ini menyimpan catatan waktu (timestamp) dari setiap request pengguna dalam sebuah log atau list.
Saat request baru masuk, sistem akan:
Menghapus semua timestamp yang lebih tua dari durasi limit (misal, lebih tua dari 1 menit yang lalu).
Menghitung jumlah timestamp yang tersisa.
Jika jumlahnya kurang dari limit, request diizinkan dan timestamp baru ditambahkan ke log.
Ilustrasi / Analogi
Bayangkan sebuah satpam yang menjaga pintu dengan aturan: "Hanya 5 orang boleh masuk dalam 60 detik terakhir."
Satpam memegang papan tulis yang mencatat waktu persis setiap orang masuk (misal: 10:01:15, 10:01:30, 10:01:45, ...).
Saat Anda datang jam 10:02:00:
Satpam menghapus semua catatan waktu sebelum 10:01:00 (karena sudah > 60 detik lalu).
Dia menghitung sisa catatan di papan.
Jika sisa catatan ada 4, Anda diizinkan masuk, dan satpam mencatat waktu Anda: 10:02:00.
Jika sisa catatan sudah ada 5, Anda ditolak.
Flow Algoritma
Sebuah request masuk dari pengguna (IP
1.2.3.4).Ambil log (list timestamp) untuk
1.2.3.4.Tentukan batas waktu (misal,
WaktuSekarang - 60 detik).Hapus semua timestamp dari log yang lebih tua dari batas waktu tersebut.
Hitung jumlah timestamp yang tersisa (
count).Jika
count<limit:Tambahkan timestamp baru (
WaktuSekarang) ke log.Izinkan request.
Jika
count>=limit:Tolak request.
Kelebihan
Sangat Akurat: Ini adalah implementasi rate limiting yang paling presisi. Tidak ada masalah edge burst sama sekali.
Kekurangan & Problem
Sangat Boros Memori: Harus menyimpan timestamp untuk setiap request. Jika limit adalah 10.000 request/jam, Anda harus menyimpan 10.000 timestamp per pengguna.
Mahal Secara Komputasi: Setiap request memicu operasi baca, filter/hapus, dan tulis pada sebuah list. Ini bisa menjadi lambat, terutama pada sistem terdistribusi (butuh locking yang mahal).
3. Sliding Window Counter (Penghitung Jendela Geser)
Ini adalah algoritma hybrid yang mencoba mendapatkan efisiensi memori dari Fixed Window dan akurasi dari Sliding Log. Ini adalah salah satu yang paling populer di dunia nyata.
Penjelasan
Algoritma ini adalah kompromi cerdas. Kita tetap menggunakan counter (seperti Fixed Window), tapi kita "menghaluskan" perhitungannya dengan melihat window sebelumnya.
Caranya adalah dengan memperkirakan jumlah request di "jendela geser" dengan menggunakan data dari dua window tetap (window saat ini dan window sebelumnya).
Rumus Perkiraan:
Rate = (Counter_Window_Sebelumnya * Persentase_Window_Sebelumnya_yg_Tersisa) + Counter_Window_Saat_Ini
💡 Ilustrasi / Analogi
Kembali ke loket 100 tiket/jam.
Data yang disimpan:
Jam 09:00 - 09:59: Terjual 80 tiket.
Jam 10:00 - 10:59: Terjual 40 tiket (sejauh ini).
Sebuah request datang pada jam 10:15:00.
Window jam 10:00 baru berjalan 25% (15 dari 60 menit). Window jam 09:00 masih "relevan" 75% sisanya.
Satpam menghitung:
Dari jam 09:00: (80 tiket * 75% relevan) = 60
Dari jam 10:00: (40 tiket) = 40
Total perkiraan = 60 + 40 = 100
Karena sudah 100, request baru ditolak. Ini jauh lebih akurat daripada Fixed Window.
Flow Algoritma
Sebuah request masuk.
Ambil counter untuk window saat ini (
C_current) dan window sebelumnya (C_previous).Hitung persentase window saat ini yang telah berlalu (misal, 15 detik dari 60 detik = 25% atau 0.25).
Hitung persentase tumpang tindih dari window sebelumnya (
percentage_previous = 1 - 0.25 = 0.75).Hitung estimasi count =
(C_previous * percentage_previous) + C_current.Jika
count<limit:Naikkan
C_current(incrementC_current++).Izinkan request.
Jika
count>=limit:Tolak request.
Kelebihan
Keseimbangan Terbaik: Memberikan akurasi yang sangat baik (hampir se-akurat Sliding Log) dengan penggunaan memori yang sangat efisien (hanya 2 counter per pengguna).
Performa Tinggi: Tidak ada pemrosesan list yang berat, hanya matematika sederhana.
Kekurangan & Problem
Tetap Estimasi: Ini bukan 100% akurat. Masih ada kemungkinan burst kecil di edge case tertentu, tapi jauh lebih baik daripada Fixed Window.
Sedikit lebih rumit untuk diimplementasikan daripada Fixed Window.
Token Bucket (Ember Token)
Algoritma ini sangat populer dan berfokus pada fleksibilitas, terutama dalam mengizinkan burst (lonjakan).
Penjelasan
Bayangkan sebuah ember (bucket) dengan kapasitas tetap. Ember ini secara konstan diisi ulang dengan "token" pada kecepatan yang tetap (misal, 10 token per detik).
Setiap request yang masuk harus "membayar" 1 token dari ember.
Jika ember penuh, token baru yang datang akan dibuang (tidak meluap).
Jika request datang dan ember kosong, request ditolak.
Kuncinya: Algoritma ini mengizinkan lonjakan request. Jika ember memiliki kapasitas 100 token dan sedang penuh, pengguna bisa langsung mengirim 100 request sekaligus (menghabiskan semua token). Setelah itu, mereka harus menunggu ember diisi ulang (10 request/detik).
Ilustrasi / Analogi
Ini adalah analogi klasiknya:
Anda punya ember (kapasitas 100 token).
Ada keran yang meneteskan 10 token/detik ke ember.
Setiap request yang mau lewat harus mengambil 1 token.
Jika Anda tidak mengirim request selama 10 detik, ember akan terisi penuh (100 token).
Anda kemudian bisa mengirim 100 request secara instan.
Setelah itu, ember Anda kosong. Anda harus menunggu 1 detik untuk bisa mengirim 10 request lagi (atau 0.1 detik untuk 1 request).
⚙Flow Algoritma
Setiap pengguna punya bucket dengan:
capacity,refill_rate,current_tokens, danlast_refill_timestamp.Saat request masuk:
Hitung berapa lama sejak refill terakhir:
duration = now - last_refill_timestamp.Hitung token baru yang harus ditambahkan:
new_tokens = duration * refill_rate.Update token di ember:
current_tokens = min(capacity, current_tokens + new_tokens).Update
last_refill_timestamp = now.Jika
current_tokens>= 1:Kurangi token:
current_tokens = current_tokens - 1.Izinkan request.
Jika
current_tokens< 1:Tolak request.
Kelebihan
Mengizinkan Burst: Sangat baik untuk traffic yang tidak merata. Memberi toleransi pada lonjakan sesaat selama rata-rata jangka panjangnya tetap terjaga.
Sangat fleksibel (bisa mengatur kapasitas burst dan rate jangka panjang).
Mudah dipahami.
Kekurangan & Problem
Dua Parameter: Membutuhkan penyesuaian 2 parameter (bucket size dan refill rate) yang bisa jadi rumit untuk diseimbangkan.
Membutuhkan lebih banyak state (data) untuk disimpan per pengguna dibandingkan Sliding Window Counter.
5. Leaky Bucket (Ember Bocor)
Algoritma ini adalah kebalikan dari Token Bucket. Fokusnya bukan mengizinkan burst, tapi menghaluskan burst.
Penjelasan
Bayangkan sebuah ember yang memiliki lubang kecil di bagian bawahnya. Air (request) keluar dari lubang itu dengan kecepatan yang konstan (misal, 10 tetes/detik).
Request yang masuk adalah air yang dituangkan ke dalam ember.
Sebuah processor akan mengambil request dari ember pada rate yang tetap (lubang bocor).
Jika request datang terlalu cepat (air dituang lebih cepat daripada lubang bocor), ember akan penuh.
Jika ember sudah penuh, request baru (air baru) akan tumpah (ditolak).
Intinya, algoritma ini menggunakan Antrian (Queue). Request dimasukkan ke antrian, dan diproses satu per satu pada rate yang stabil.
Ilustrasi / Analogi
Seperti namanya.
Air (request) masuk ke ember dari atas.
Ember mengeluarkan air (processing) dari bawah dengan rate 10 tetes/detik, tidak peduli seberapa deras air di atas.
Jika air dituang terlalu deras (burst), permukaan air di ember akan naik (antrian memanjang).
Jika ember penuh (antrian penuh), air yang baru dituang akan ditolak.
Flow Algoritma
Gunakan antrian (Queue) FIFO (First-In, First-Out) dengan kapasitas tetap (misal, 100).
Sebuah worker (proses terpisah) mengambil item dari antrian pada rate tetap (misal, 10 request/detik).
Saat request masuk:
Periksa jumlah item di antrian (
queue_size).Jika
queue_size<capacity:Masukkan request ke dalam antrian (Enqueue).
Request akan diproses oleh worker nanti.
Jika
queue_size>=capacity:Tolak request (antrian penuh).
Kelebihan
Menjamin Rate Pemrosesan yang Stabil: Sangat baik untuk melindungi layanan backend (misal, database) yang tidak boleh menerima lonjakan traffic. Traffic "dihaluskan" (smoothed out).
Sangat terprediksi.
Kekurangan & Problem
Tidak Mengizinkan Burst: Request yang datang dalam lonjakan akan mengalami latensi (karena harus mengantri) atau langsung ditolak. Ini tidak ideal jika burst sesaat sebenarnya bisa ditangani.
Request yang lama di antrian bisa jadi timeout sebelum sempat diproses.
Implementasinya lebih kompleks karena membutuhkan queue dan worker yang berjalan terpisah.
Tabel Perbandingan
Algoritma
Akurasi
Efisiensi Memori
Mengizinkan Burst?
Kompleksitas
Kasus Penggunaan Ideal
Fixed Window
Rendah
Sangat Tinggi
Tidak (dan menyebabkan burst di tepi)
Sangat Rendah
Analitik sederhana, rate limiting yang tidak kritis.
Sliding Log
Sempurna
Sangat Rendah
Ya (secara alami)
Tinggi
Saat akurasi 100% adalah harga mati (jarang).
Sliding Window
Tinggi
Tinggi
Tidak (menghaluskan burst di tepi)
Sedang
Rate limiting API publik standar. (Contoh: Nginx, Redis)
Token Bucket
Tinggi
Sedang
Ya (Sengaja)
Sedang
Saat Anda ingin mengizinkan lonjakan (misal, upload file). (Contoh: AWS)
Leaky Bucket
Tinggi
Sedang
Tidak (Menghaluskan)
Tinggi
Melindungi worker atau database dari lonjakan input.
Mana yang Paling Sempurna?
Tidak ada satu algoritma yang "paling sempurna" untuk semua kasus.
Untuk Rate Limiting API secara umum, Sliding Window Counter sering dianggap sebagai pilihan terbaik karena memberikan keseimbangan yang nyaris sempurna antara akurasi, performa, dan efisiensi memori.
Jika Anda ingin mengizinkan burst (karena traffic pengguna memang naik-turun), Token Bucket adalah pilihan yang lebih baik.
Last updated