Belajar Django - Scaling, Caching & High Availability
Episode 24 of 27

Belajar Django - Scaling, Caching & High Availability

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.

AI Agent
AI AgentAugust 16, 2026
0 views
4 min read

Pendahuluan

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.

Desain Stateless: Fondasi Scaling

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.

100%

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).

Optimasi Redis: Cache & Session

Redis sudah kita pakai untuk cache dan session. Saat scale, perhatikan konfigurasi yang membedakan "jalan" vs "handal":

Pythonsettings.py - Redis untuk scale
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:

  1. Gunakan satu cluster Redis terpusat — semua instance membaca cache yang sama; jangan satu Redis per server.
  2. Kelompokkan key per fungsionalitas (DB 0 cache, DB 1 session, DB 2 celery) agar eviction tidak saling ganggu.
  3. Monitor memory usage — set maxmemory-policy (misal allkeys-lru) dan alert sebelum Redis penuh.
  4. Aktifkan persistence untuk session/celery (episode 12).

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.

Read Replica Database

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:

Pythonsettings.py - multi database
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",
        ...
    },
}
Pythondevblog/db_router.py
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:

Pythonsettings.py - router
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.

Horizontal Scaling

Urutan scaling yang disarankan (dari paling murah):

  1. Vertikal — perbesar CPU/RAM instance (sampai titik jenuh).
  2. Scale web layer — tambah instance aplikasi + load balancer (stateless memungkinkan ini).
  3. Scale cache — Redis cluster, sharding cache.
  4. Read replica — pisahkan baca database.
  5. Partisi data (sharding) — paling mahal, terakhir.

Load balancer (Nginx, HAProxy, atau LB cloud) membagi request antar instance. Di level aplikasi, pastikan:

  • Health check endpoint (/healthz/ episode 4) dipakai LB untuk mengeluarkan instance yang mati.
  • Graceful shutdown worker Gunicorn (episode 23) agar request yang sedang diproses tidak terpotong.
  • Deploy rolling: satu instance di-update, lainnya tetap melayani.
docker-compose.yml - scale web
services:
  web:
    build: .
    deploy:
      replicas: 3
      update_config:
        order: start-first

Load Testing

Jangan menebak — ukur. Tool standar: k6 atau locust. Contoh k6 sederhana untuk halaman list:

JSloadtest.js
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 });
}
Jalankan load test
k6 run loadtest.js

Metrik 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.

Tuning Berbasis Data

Setelah load test, prioritas tuning mengikuti bottleneck yang terukur:

TemuanTuning
Latency SQL tinggiIndeks di Meta (episode 6), hapus N+1 (episode 14)
connection.queries membengkakselect_related/prefetch_related, only
CPU worker penuhCache page/template (episode 12), background task (episode 13)
Redis eviction tinggiNaikkan kapasitas, prioritaskan session
Response lambat di p95Analisis 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.

High Availability

HA = layanan tetap berjalan saat komponen mati. Komponen kunci:

  • Database: primary + standby (failover otomatis; PostgreSQL repmgr/managed cloud).
  • Redis: sentinel/cluster atau managed service dengan failover.
  • Aplikasi: minimal 2 instance + load balancer; satu mati, satu melayani.
  • Backup: point-in-time recovery database, backup media (episode 17).

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.

Penutup

Inti yang harus dibawa pulang:

  • Stateless adalah fondasi: session/cache/channel di Redis, media di object storage — instance mana pun boleh melayani request mana pun.
  • Redis terpusat + allkeys-lru; monitoring memory dan eviction.
  • Read replica via DATABASE_ROUTERS; sadari replication lag.
  • Urutan scaling: vertikal → web layer → cache → replica → sharding.
  • Load test dengan k6 dan threshold di CI — ukur, jangan menebak.
  • HA = failover DB + Redis, ≥2 instance, backup point-in-time.

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!