Lewati ke konten

Arsitektur & Desain Sistem

distlimit dirancang khusus untuk mengatasi masalah bottleneck performa, risiko thundering herd, dan contention lock global yang umum terjadi pada library rate-limiting tradisional.


flowchart TD
    Req[HTTP / gRPC Incoming Request] --> Shield[Trusted Proxies Check]
    Shield --> Limiter[distlimit Limiter]

    subgraph CoreEngine [Core Execution Engine]
        direction LR
        Limiter --> Alg[Pluggable Algorithms]
        Limiter --> Driver[Storage Drivers]
    end

    CoreEngine --> Observer[metrics Observer Engine]

Go map standar yang dilindungi oleh satu sync.RWMutex mengalami lock contention parah di bawah beban kerja multi-threaded tinggi.

distlimit menyelesaikan masalah ini dengan membagi penyimpanan key ke dalam 64 shard independen:

type Driver struct {
shards [64]*shard
}
  • Indeks Shard: Penentuan posisi key dihitung via hashing fnv32a: shardIndex = hash(key) % 64.
  • Granularitas Lock: Setiap shard memiliki sync.RWMutex sendiri. Request konkuren yang menargetkan key berbeda akan mengakses shard yang berbeda secara paralel, menghilangkan bottleneck lock global.
  • Eliminasi Jeda GC: Struktur entri penyimpanan dioptimalkan untuk pengaplikasian ulang di memori, menjaga alokasi tetap pada 0 B/op.

2. Redis Cluster Hash Tags {} & Skrip Lua Atomik

Section titled “2. Redis Cluster Hash Tags {} & Skrip Lua Atomik”

Rate limiting terdistribusi membutuhkan evaluasi status secara atomik di seluruh node Redis untuk mencegah race condition.

Saat beroperasi di dalam Redis Cluster, operasi yang melibatkan beberapa key harus menargetkan hash slot yang sama untuk menghindari error CROSSSLOT. distlimit membungkus nama key Redis dalam hash tag: distlimit:{key}.

Redis menghitung hash slot hanya menggunakan karakter di dalam {}. Ini menjamin semua operasi untuk suatu key rate-limiting dieksekusi pada node slot cluster yang sama.

Semua transisi status algoritma (refill token, pembersihan timestamp, inkremen counter) ditulis dalam skrip Lua teroptimasi. Redis mengeksekusi skrip Lua secara atomik dalam single thread, menjamin zero race condition antar node aplikasi terdistribusi.


3. Dual-Tier Hybrid Driver & Half-Open Circuit Breaker

Section titled “3. Dual-Tier Hybrid Driver & Half-Open Circuit Breaker”

Driver Hybrid melapisi driver Redis utama di atas driver fallback In-Memory sekunder:

flowchart TD
    Req[Incoming Request] --> Hybrid[Hybrid Driver]

    Hybrid -->|Primary OK| Redis[Redis Driver]
    Hybrid -->|Primary Fail| CB[Circuit Breaker - Cool-Off State]

    Redis -->|Error| CB
    CB --> Memory[Failover Memory Driver]
  1. Normal Flow (Closed): Semua pemeriksaan rate limit dieksekusi ke Redis Primary.
  2. Failover (Open): Jika Redis mengembalikan error jaringan atau timeout koneksi, Circuit Breaker langsung terbuka. Request langsung dialihkan ke driver In-Memory 64-Shard lokal tanpa memblokir request client.
  3. Durasi Cool-Off: Driver berada dalam mode fallback selama durasi terkonfigurasi (default: 5 detik), meredam panggilan jaringan yang gagal ke Redis.
  4. Half-Open Probing: Setelah timer cool-off habis, driver Hybrid memasuki mode Half-Open. Satu goroutine melakukan uji coba (probe) ke Redis menggunakan lock atomik CompareAndSwap(false, true).
    • Jika uji coba berhasil, Circuit Breaker menutup dan operasi normal ke Redis dilanjutkan.
    • Jika uji coba gagal, timer cool-off di-reset. Ini mencegah lonjakan Thundering Herd ke node Redis yang sedang pulih.

distlimit menggunakan atomic.Pointer[Policy] untuk menyimpan aturan rate limit yang aktif:

type Limiter struct {
policy atomic.Pointer[Policy]
}

Saat limiter.UpdatePolicy(newLimit, newWindow) dipanggil saat runtime:

  1. Struct Policy baru dialokasikan.
  2. policy.Store() melakukan swap pointer secara atomik.

Goroutine yang mengeksekusi AllowKey() membaca pointer via policy.Load(). Ini memberikan kemampuan pembaruan aturan secara realtime tanpa downtime dan tanpa lock contention dengan latensi nanodetik $O(1)$.