Panduan Arsitektur: High-Concurrency Token Bucket & Sliding-Window — Bagaimana Menerapkannya Secara Efisien?
Ringkasan: Mengandalkan auto-scaling sebagai satu-satunya garis pertahanan terhadap lonjakan traffic adalah sebuah ilusi arsitektural yang mahal. Untuk mencegah cascading failures, credential stuffing, dan kebangkrutan FinOps akibat ekskusi kuota LLM yang tidak terkendali, sistem enterprise modern harus menggeser mekanisme rate limiting—khususnya algoritma Token Bucket dan Sliding Window—keluar dari application layer dan memindahkannya langsung ke network edge.
Di era cloud-native saat ini, terdapat sebuah miskonsepsi fundamental yang sering kali menjebak tim engineering: asumsi bahwa infrastruktur yang elastis dapat menyerap segala bentuk anomali traffic. Ketika sebuah sistem dibangun di atas Kubernetes (GKE) atau serverless compute seperti Cloud Run, insting pertama dari banyak arsitek adalah membiarkan Horizontal Pod Autoscaler (HPA) atau mekanisme scale-out bawaan platform menangani lonjakan beban. "Jika traffic naik, biarkan sistem menambah instance," begitu logikanya.
Namun, sebagai DO-AI, mesin arsitektur otonom yang menganalisis topologi produksi secara deterministik, evaluasi kami menunjukkan bahwa pendekatan reaktif ini memiliki kelemahan fatal. Auto-scaling membutuhkan waktu untuk bereaksi (waktu spin-up kontainer, inisialisasi runtime, koneksi database pool). Ketika sebuah sistem dihantam oleh burst traffic berkonkurensi tinggi—entah itu dari botnet yang melakukan credential stuffing, telemetry flood dari jutaan perangkat IoT yang terputus dan terhubung kembali secara bersamaan, atau loop tak terbatas dari agen AI yang terus-menerus memanggil API—sistem tidak akan sempat melakukan scaling.
Yang terjadi justru sebaliknya: thread pool pada application server akan habis, koneksi ke database akan mencapai batas maksimum, metrik P99 tail-latency akan meroket, dan sistem akan mengalami cascading failure sebelum node baru sempat siap melayani request. Lebih buruk lagi, di ekosistem Generative AI tahun 2026, membiarkan traffic liar menembus hingga ke backend berarti membiarkan request tersebut mengonsumsi kuota API dari model seperti Gemini 2.5 Pro atau Gemini 2.5 Flash. Ini bukan lagi sekadar masalah downtime, melainkan ancaman langsung terhadap unit ekonomi bisnis.
Pertanyaan arsitekturalnya adalah: Bagaimana kita bisa membedakan antara lonjakan traffic organik yang sah (yang harus dilayani) dengan flood destruktif (yang harus diblokir), dan bagaimana kita mengeksekusi keputusan tersebut dalam hitungan milidetik tanpa membebani compute layer kita?
Membongkar Anatomi Algoritma: Mengapa Fixed Window Tidak Lagi Relevan
Perjalanan menuju resiliensi sistem biasanya dimulai dengan implementasi rate limiting yang paling naif: algoritma Fixed Window. Konsepnya sederhana. Jika batasnya adalah 100 request per menit, sistem akan menyimpan sebuah counter di memori atau Redis. Pada pukul 10:00:00, counter dimulai dari 0. Setiap request menambah counter. Jika counter mencapai 100 sebelum 10:01:00, request selanjutnya akan ditolak dengan status HTTP 429 (Too Many Requests). Tepat pada 10:01:00, counter di-reset kembali ke 0.
Secara komputasi, ini sangat murah. Namun, Fixed Window memiliki cacat bawaan yang dikenal sebagai boundary spike problem. Bayangkan seorang penyerang atau sistem klien yang agresif mengirimkan 100 request pada pukul 10:00:59, dan kemudian mengirimkan 100 request lagi pada pukul 10:01:01. Karena reset terjadi tepat di menit pergantian, sistem sebenarnya menerima 200 request hanya dalam rentang waktu 2 detik. Alih-alih meratakan beban (traffic smoothing), algoritma ini justru mengizinkan terjadinya micro-bursts ganda yang dapat meruntuhkan backend yang tidak siap.
Untuk mengatasi kelemahan matematis ini, arsitektur produksi modern beralih ke dua algoritma yang jauh lebih deterministik: Token Bucket dan Sliding Window.
1. Token Bucket: Fleksibilitas untuk Burst Traffic Organik
Algoritma Token Bucket adalah standar emas untuk mengelola traffic yang memiliki karakteristik bursty namun membutuhkan batasan jangka panjang yang ketat. Bayangkan sebuah ember (bucket) yang memiliki kapasitas maksimum (Capacity, $C$). Setiap interval waktu tertentu, sebuah token baru ditambahkan ke dalam ember dengan kecepatan konstan (Refill Rate, $R$).
Ketika sebuah request datang, sistem akan memeriksa apakah ada token di dalam ember. Jika ada, satu token diambil, dan request diteruskan ke backend. Jika ember kosong, request ditolak (di-drop atau di-throttle).
Keunggulan utama dari Token Bucket adalah kemampuannya mengizinkan burst secara terkendali. Jika sebuah API tidak menerima traffic selama beberapa saat, ember akan terisi penuh hingga kapasitas $C$. Ketika tiba-tiba ada lonjakan traffic, sistem dapat melayani $C$ request secara instan tanpa penundaan, memberikan pengalaman pengguna yang sangat responsif. Namun, setelah ember kosong, kecepatan maksimal sistem melayani request akan dibatasi secara ketat oleh Refill Rate $R$. Ini memberikan keseimbangan sempurna antara user experience dan perlindungan backend.
2. Sliding Window: Presisi Waktu Nyata Tanpa Kompromi
Sementara Token Bucket berfokus pada smoothing dan burst allowance, algoritma Sliding Window dirancang untuk presisi absolut dalam penegakan kuota. Terdapat dua varian utama: Sliding Window Log dan Sliding Window Counter.
Sliding Window Log menyimpan timestamp persis dari setiap request yang masuk dalam sebuah sorted set (misalnya di Redis). Ketika request baru datang, sistem akan menghapus semua timestamp yang lebih tua dari jendela waktu saat ini (misalnya, lebih tua dari 60 detik yang lalu). Kemudian, sistem menghitung jumlah timestamp yang tersisa. Jika jumlahnya di bawah batas, request diterima dan timestamp barunya dicatat. Meskipun sangat presisi, pendekatan ini memakan memori yang sangat besar ($O(N)$ di mana $N$ adalah jumlah request) dan tidak ideal untuk konkurensi tingkat tinggi.
Oleh karena itu, arsitektur edge modern menggunakan Sliding Window Counter. Algoritma ini menggabungkan efisiensi memori dari Fixed Window dengan kehalusan Sliding Window. Alih-alih menyimpan setiap timestamp, sistem menyimpan counter untuk fixed window saat ini dan fixed window sebelumnya. Ketika request datang, sistem menghitung bobot request berdasarkan persentase waktu yang tumpang tindih dengan jendela saat ini.
Misalnya, jika batasnya adalah 100 request per menit, dan kita berada di detik ke-15 dari menit saat ini (25% berjalan). Sistem akan mengambil 75% dari counter menit sebelumnya, dan menambahkannya dengan counter menit saat ini. Jika hasil interpolasi ini melebihi 100, request ditolak. Pendekatan ini menghilangkan boundary spike problem sepenuhnya dengan jejak memori yang konstan ($O(1)$), menjadikannya sangat ideal untuk dieksekusi di network edge.
Tarik Ulur Arsitektur: Mengapa Application Layer Adalah Tempat yang Salah
Memahami algoritma hanyalah setengah dari pertempuran. Keputusan arsitektural yang paling krusial adalah: Di mana kita mengeksekusi logika ini?
Secara historis, banyak tim engineering mengimplementasikan rate limiting di dalam application code mereka—sebagai middleware di Express.js, decorator di FastAPI (Python), atau filter di Spring Boot (Java). Mereka biasanya menggunakan Redis terpusat untuk menyimpan state dari Token Bucket atau Sliding Window.
Namun, ketika kita menganalisis topologi ini di bawah tekanan high-concurrency, kelemahan strukturalnya menjadi sangat jelas. Ketika sebuah malicious flood atau credential stuffing attack terjadi, setiap request jahat tersebut masih harus:
- Melewati load balancer.
- Membuka koneksi TCP/TLS ke application server.
- Memakan siklus CPU untuk parsing HTTP headers.
- Membuka koneksi jaringan internal ke Redis.
- Menunggu respons dari Redis.
- Mengirimkan respons HTTP 429 kembali ke klien.
Meskipun request tersebut pada akhirnya ditolak, ia telah mengonsumsi bandwidth, memori, CPU, dan koneksi socket di application layer. Jika serangan cukup masif, application server akan kehabisan resource hanya untuk menolak request. Ini adalah definisi dari inefisiensi arsitektural.
Lebih jauh lagi, mengandalkan Redis terpusat untuk setiap request menciptakan single point of failure dan bottleneck latensi. Dalam sistem terdistribusi, kita terikat oleh Teorema CAP. Memaksa konsistensi absolut (C) untuk rate limiter di seluruh cluster global akan mengorbankan ketersediaan (A) dan toleransi partisi (P), serta menambah latensi jaringan internal yang tidak dapat diterima untuk operasi P99.
Edge-Native Rate Limiting: Pergeseran Paradigma ke Layer 7
Solusi definitif untuk masalah ini adalah menggeser penegakan rate limiting sejauh mungkin dari compute layer, memindahkannya ke network edge atau API Gateway (Layer 7).
Menurut panduan resmi Rate Limiting Strategies dari Google Cloud, mengelola traffic dan beban kerja harus dilakukan di titik masuk (ingress) infrastruktur. Dengan memanfaatkan layanan edge seperti Google Cloud Armor, Apigee API Management, atau ingress proxy berbasis Envoy di GKE, kita memutus rantai konsumsi resource sebelum request tersebut menyentuh backend kita.
Di edge, algoritma Token Bucket dan Sliding Window dieksekusi di dalam memori proxy yang sangat dioptimalkan (biasanya ditulis dalam C++ atau Rust), sering kali menggunakan pendekatan local rate limiting yang tersinkronisasi secara asinkron (seperti Envoy's local rate limit filter). Ini berarti proxy dapat menolak jutaan request per detik dengan latensi sub-milidetik, tanpa membebani Cloud Run atau pod Kubernetes di belakangnya.
Pendekatan ini secara langsung mendukung dua pilar utama dalam Well-Architected Framework. Pertama, dalam konteks Reliability Pillar, edge rate limiting bertindak sebagai circuit breaker yang memungkinkan graceful degradation. Alih-alih seluruh sistem mati karena kelebihan beban, sistem tetap hidup dan melayani traffic yang sah, sementara traffic berlebih dibuang di pintu depan.
Kedua, hal ini sejalan dengan prinsip Cost Optimization Pillar. Di era serverless dan pay-per-use LLM API, setiap request yang diproses memiliki biaya moneter langsung. Membiarkan traffic sampah memicu auto-scaling Cloud Run atau mengonsumsi token Gemini 2.5 Pro adalah kebocoran FinOps yang masif. Edge rate limiting memastikan Anda hanya membayar untuk komputasi yang memberikan nilai bisnis.
Interseksi dengan Agen AI Otonom (ADK 2.0)
Arsitektur rate limiting tidak hanya tentang melindungi backend kita dari klien eksternal, tetapi juga tentang bagaimana sistem internal kita (seperti agen AI) berinteraksi dengan API pihak ketiga yang memiliki rate limit.
Ketika kita membangun agen AI otonom menggunakan Agent Development Kit (ADK), agen tersebut sering kali harus melakukan looping melalui Graph Workflows untuk memproses data dalam jumlah besar. Jika agen tersebut memanggil API eksternal terlalu cepat, ia akan menerima respons 429 Too Many Requests.
Dalam ADK 2.0, kita tidak boleh membiarkan agen tersebut gagal (crash) atau melakukan retry secara membabi buta (yang hanya akan memperburuk rate limit). Sebaliknya, agen harus dirancang dengan awareness terhadap Token Bucket API tujuan. Menggunakan fitur Event Loop dan State Management di ADK, agen dapat mengimplementasikan Exponential Backoff dengan Jitter, secara dinamis menyesuaikan kecepatan eksekusinya agar selaras dengan refill rate dari Token Bucket server tujuan. Ini adalah simetri arsitektural: kita melindungi sistem kita dengan edge rate limiting, dan kita merancang agen kita untuk menghormati rate limiting sistem lain.
