Mempersiapkan aplikasi tumbuh: desain stateless agar multi-instance aman, memaksimalkan Redis untuk cache dan session, read replica database dengan router, load balancing horizontal, serta praktik load testing dan tuning berbasis data.

Deployment di episode 23 sudah jalan di satu server. Sekarang bayangkan traffic mulai menumpuk: satu instance tidak cukup. Di episode ini kita membahas Scaling, Caching & High Availability — bagaimana aplikasi Django dirancang agar bisa ditambah instance tanpa rusak, dan bagaimana mengukur kapan harus scale.
Mengapa topik ini penting? Karena scale bukan sekadar menambah server — tanpa desain yang benar (stateless, shared cache/session, read replica), menambah instance justru bikin aplikasi makin tidak konsisten. High availability adalah kemampuan bertahan saat komponen mati, dan itu dibangun dari keputusan arsitektur, bukan dari membeli lebih banyak hardware.
Stateless berarti aplikasi tidak menyimpan data di memori/disk lokal yang menjadi "kebenaran". Semua state yang dibutuhkan request (session, cache) tinggal di layanan bersama (Redis). Ini syarat mutlak horizontal scaling: instance mana pun boleh menerima request mana pun.
Jika session tersimpan di memori worker (default runserver), user yang login di instance A bisa ter-logout begitu request berikutnya jatuh ke instance B. Itu sebabnya kita sudah memindahkan session ke Redis di episode 12 dan channel layer ke Redis di episode 21 — semua keputusan itu membayar di episode ini.
Warning
Audit aplikasi untuk "state lokal tersembunyi": file yang ditulis ke disk lokal (MEDIA_ROOT default), LocMemCache yang masih terpasang, atau task Celery yang menyimpan hasil di memori. Semua itu merusak konsistensi multi-instance. Aturannya: apa pun yang harus dibagikan antar instance → Redis/database/object storage (episode 17).
Redis sudah kita pakai untuk cache dan session. Saat scale, perhatikan konfigurasi yang membedakan "jalan" vs "handal":
CACHES = {
"default": {
"BACKEND": "django.core.cache.backends.redis.RedisCache",
"LOCATION": "redis://redis:6379/1",
"TIMEOUT": 300,
"OPTIONS": {
"db": 1,
"parser_class": "redis.connection.HiredisParser",
},
}
}Langkah praktis:
maxmemory-policy (misal allkeys-lru) dan alert sebelum Redis penuh.Dengan allkeys-lru, Redis meng-evict key paling jarang dipakai saat penuh — cache halaman blog (yang boleh hilang) mengorbankan diri sebelum session (yang tidak boleh hilang) ter-evict.
Saat database jadi bottleneck, pisahkan baca dan tulis. Primary menerima write; replica melayani query baca (sinkronisasi async via WAL). Django mendukung multi-database dengan router:
DATABASES = {
"default": {
"ENGINE": "django.db.backends.postgresql",
"NAME": "devblog",
"HOST": "primary.example.com",
...
},
"replica": {
"ENGINE": "django.db.backends.postgresql",
"NAME": "devblog",
"HOST": "replica.example.com",
...
},
}class PrimaryReplicaRouter:
def db_for_read(self, model, **hints):
return "replica"
def db_for_write(self, model, **hints):
return "default"
def allow_migrate(self, db, app_label, **hints):
return db == "default"db_for_read mengirim semua SELECT ke replica, db_for_write mengirim INSERT/UPDATE ke primary, allow_migrate membatasi migrasi ke primary saja. Daftarkan router di settings:
DATABASE_ROUTERS = ["devblog.db_router.PrimaryReplicaRouter"]Tip
Read replica punya replication lag — data yang baru ditulis belum tentu langsung terlihat di replica. Pola "read-your-owns-write" memakai using("default") untuk query yang harus konsisten dengan write baru (misal halaman setelah submit form), atau me-lewatkan replica untuk operasi kritis. Jangan menerima lag tanpa memikirkan dampaknya terhadap UX.
Urutan scaling yang disarankan (dari paling murah):
Load balancer (Nginx, HAProxy, atau LB cloud) membagi request antar instance. Di level aplikasi, pastikan:
/healthz/ episode 4) dipakai LB untuk mengeluarkan instance yang mati.services:
web:
build: .
deploy:
replicas: 3
update_config:
order: start-firstJangan menebak — ukur. Tool standar: k6 atau locust. Contoh k6 sederhana untuk halaman list:
import http from "k6/http";
import { check } from "k6";
export const options = {
stages: [
{ duration: "1m", target: 50 }, // ramp ke 50 VU
{ duration: "3m", target: 50 }, // tahan
{ duration: "1m", target: 0 }, // turunkan
],
thresholds: {
http_req_duration: ["p(95)<500"],
http_req_failed: ["rate<0.01"],
},
};
export default function () {
const res = http.get("https://devblog.example.com/");
check(res, { "status 200": (r) => r.status === 200 });
}k6 run loadtest.jsMetrik kunci yang dicatat: RPS, latency p95/p99, dan error rate. Threshold (p(95) < 500ms) membuat test gagal otomatis jika performa menurun — jadikan bagian dari CI (episode 23) untuk mencegah regresi performa.
Warning
Uji beban dengan data mendekati produksi. Load test terhadap database kosong hanya mengukur jaringan — performa asli muncul saat tabel berisi jutaan baris dan cache sudah panas/dingin secara realistis. Gunakan juga data yang merepresentasikan pola akses nyata (baca-dominan untuk blog), bukan skenario sintetis.
Setelah load test, prioritas tuning mengikuti bottleneck yang terukur:
| Temuan | Tuning |
|---|---|
| Latency SQL tinggi | Indeks di Meta (episode 6), hapus N+1 (episode 14) |
connection.queries membengkak | select_related/prefetch_related, only |
| CPU worker penuh | Cache page/template (episode 12), background task (episode 13) |
| Redis eviction tinggi | Naikkan kapasitas, prioritaskan session |
| Response lambat di p95 | Analisis query lambat, tambah replica |
Setiap perubahan harus diuji ulang dengan load test yang sama — metrik yang sama, sebelum/sesudah. Inilah loop "measure → optimize → measure" yang membuat tuning tidak menebak.
HA = layanan tetap berjalan saat komponen mati. Komponen kunci:
repmgr/managed cloud).Tidak semua komponen harus HA sejak hari pertama — mulai dari yang paling sering gagal dan paling mahal dampaknya. Untuk blog devblog, prioritas: backup database otomatis, minimal 2 web instance, dan Redis ter-manage.
Inti yang harus dibawa pulang:
allkeys-lru; monitoring memory dan eviction.DATABASE_ROUTERS; sadari replication lag.Di episode 25 selanjutnya kita menengok ekosistem: Ekosistem & Tren Modern 2026 — Django 6.1 & 5.2 LTS, perbandingan dengan FastAPI/Flask, Ninja, HTMX/Unpoly, dan arah Django async di 2027. Sampai jumpa di episode 25!