Lonjakan beban database sering kali bukan disebabkan oleh serangan eksternal terkoordinasi, melainkan DoS internal: loop retry tanpa batas, kueri analitik liar yang lolos ke instance OLTP, atau N+1 query dari microservice yang baru dideploy. Ketika connection pool database habis, seluruh sistem lumpuh. Menambahkan kapasitas hardware bukan solusi instan saat thundering herd terjadi.
Solusi yang terbukti di skala produksi adalah menempatkan traffic controller di layer proxy (seperti Vitess VTTablet, Envoy, atau custom proxy berbasis connection pooler). Pendekatan yang dipopulerkan oleh PlanetScale ini mengisolasi database engine dari konsumsi koneksi liar melalui enforcement concurrency limit, query fingerprinting, query timeout ketat, dan circuit breaker.
Arsitektur Traffic Control di Layer Database Proxy
Pola tradisional menghubungkan pool aplikasi langsung ke listener database PostgreSQL atau MySQL. Pola traffic control menyisipkan intermediary proxy cerdas yang menguraikan (parse) AST query sebelum query tersebut mencapai engine database.
[App Pooler] -> [Database Traffic Controller / Proxy] -> [Primary/Replica DB]
|
+-> Token Bucket Rate Limiting (per-caller)
+-> Fingerprint & AST Parser
+-> Circuit Breaker & Queue ManagerProxy ini bertindak sebagai penjaga gerbang dengan aturan isolasi resource:
- Connection decoupling: Menolak pemetaan 1:1 antara koneksi client dan koneksi native database.
- State tracking: Mengidentifikasi identitas caller (service ID, IP, atau user token) dan menghitung inflight queries.
- Fast fail: Menghentikan kueri bermasalah di proxy dalam hitungan milidetik tanpa membebani CPU engine database.
1. Concurrency Limiting per Caller
Masalah utama DoS internal adalah alokasi resource yang tidak adil (noisy neighbor problem). Layanan non-kritis (misal: reporting) dapat menghabiskan thread pool DB, memblokir transaksi pembayaran.
Implementasi traffic control menerapkan concurrency token bucket per caller. Caller diidentifikasi via atribut koneksi (kredensial proxy atau SQL comment metadata seperti /*service:billing*/).
# Contoh konfigurasi traffic control proxy (YAML pseudo-spec)
callers:
- name: service_payment
max_concurrency: 50
queue_depth: 20
queue_timeout_ms: 200
priority: critical
- name: service_analytics
max_concurrency: 5
queue_depth: 0
queue_timeout_ms: 0
priority: low
global_defaults:
max_concurrency: 120
circuit_breaker:
max_inflight_threshold: 100
db_cpu_threshold_percent: 85Ketika service_analytics mencoba menjalankan 6 kueri bersamaan, kueri ke-6 langsung ditolak dengan error ERR_CALLER_LIMIT_EXCEEDED tanpa masuk ke antrean database. Sebaliknya, service_payment diizinkan antre hingga 200ms sebelum di-reject.
2. Query Fingerprinting dan Pola Berulang
Untuk memblokir query abuse secara selektif tanpa restart aplikasi, proxy harus melakukan normalisasi kueri menjadi fingerprint (hash). Mekanisme ini mirip dengan query rules pada VTTablet Vitess.
Normalisasi mengubah nilai literal menjadi placeholder:
SELECT * FROM users WHERE id = 1204 AND status = 'active';
-- Menjadi Fingerprint:
-- SELECT * FROM users WHERE id = ? AND status = ?
-- Hash: 7b92f9a1c8d3e5b0Proxy mencatat metrik per hash: laju pemanggilan (QPS), durasi eksekusi, dan latensi locking. Aturan mitigasi dapat disuntikkan secara dinamis ke proxy memory:
-- Aturan runtime pada proxy: batasi query lambat yang dipanggil agresif
PUT /v1/rules/fingerprint/7b92f9a1c8d3e5b0
{
"action": "THROTTLE",
"max_qps": 2,
"action_on_exceed": "REJECT",
"error_message": "Query pattern is throttled due to high read amplification"
}Dengan cara ini, hanya rute kode bermasalah yang terblokir sementara jalur transaksi esensial lainnya tetap berjalan normal.
3. Enforcing Strict Query Timeout
Banyak developer mengonfigurasi timeout pada level HTTP gateway (misal: 30 detik), tetapi membiarkan database timeout kosong (unlimited). Jika HTTP gateway memutus koneksi client, query database tetap berjalan sebagai orphan query yang memakan memori dan lock.
Traffic controller mewajibkan dua level timeout:
- Wait-in-Queue Timeout: Jika antrean thread lokal proxy penuh, buang kueri dalam waktu 100-300ms. Lebih baik fail fast daripada menumpuk cascade latency.
- Execution Timeout: Eksekusi query dibatasi ketat per kategori. Bacaan OLTP dibatasi maksimal 1000ms.
Pada proxy Postgres, enforcement ini dipaksakan secara dinamis pada tiap sesi yang dialokasikan:
-- Dijalankan oleh proxy engine saat sesi dipinjam
SET LOCAL statement_timeout = '800ms';
SET LOCAL lock_timeout = '200ms';
-- Query dari aplikasi berjalan di sini
SELECT * FROM orders WHERE user_id = $1 FOR UPDATE;
COMMIT;4. Circuit Breaker Saat Database Overload
Ketika beban DB mendekati titik jenuh (misalnya CPU > 85% atau storage I/O queue memuncak), traffic controller harus beralih ke mode shed load (pembuangan beban bertingkat).
type State int
const (
StateClosed State = iota
StateHalfOpen
StateOpen
)
type CircuitBreaker struct {
inflightQueryCount int64
maxInflight int64
failureThreshold int64
consecutiveErrors int64
state State
}
func (cb *CircuitBreaker) AllowQuery(priority string) bool {
if cb.state == StateOpen {
// Hanya izinkan traffic esensial saat recovery
return priority == "critical"
}
if cb.inflightQueryCount > cb.maxInflight {
if priority != "critical" {
return false // Shed non-critical traffic langsung di proxy
}
}
return true
}Dengan circuit breaker aktif, saat database berada di bawah tekanan ekstrem, proxy langsung mengembalikan error HTTP 503 / DB Connection Error ke background jobs dan worker internal, mempertahankan resource tersisa untuk checkout flow atau login.
Trade-offs dan Kesalahan Implementasi
- AST Parsing Overhead: Mem-parsing setiap syntax tree kueri di proxy memakan latensi mikrodetik CPU. Solusi: Gunakan hash caching berdasarkan exact query string sebelum parsing kompleks.
- Broken Prepared Statements: Pemisahan session yang agresif pada proxy dapat merusak
PREPAREstatements server-side jika proxy mengalokasikan backend connection yang berbeda. Solusi: Gunakan transaction pooling atau konfigurasipreferQueryMode=simplepada driver aplikasi. - Misclassified Priority: Layanan internal sering kali salah dilabeli sebagai "critical". Terapkan auditing ketat atas service mana saja yang berhak atas resource pool prioritas tinggi.
Komentar
0 komentar
Masuk ke akun kamu untuk ikut berkomentar.
Belum ada komentar
Jadilah yang pertama ikut berdiskusi!