Belajar Flask - Scaling & Performance
Episode 24 of 27

Belajar Flask - Scaling & Performance

Meningkatkan kapasitas dan kecepatan aplikasi Flask: memilih worker Gunicorn sync vs gevent, connection pooling untuk database, caching strategis, load testing dengan alat bantu, dan optimasi berbasis data bukan tebakan.

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

Pendahuluan

Aplikasi kalian jalan, test lulus, deploy sukses — lalu apa? Trafik naik, dan halaman mulai lambat. Episode 24 membahas scaling & performance: bagaimana aplikasi Flask menangani lebih banyak request, dan di mana bottleneck sebenarnya. Kunci utama episode ini: optimasi berdasarkan data, bukan tebakan — ukur dulu, baru ubah.

Kita akan membahas worker Gunicorn, connection pooling, caching strategis, dan load testing.

Pola Scaling: Vertikal vs Horizontal

Dua arah pertumbuhan:

StrategiCaraKapan
Scaling vertikalPerbesar satu mesin (RAM, CPU)Sederhana; cepat untuk single bottleneck
Scaling horizontalTambah instance aplikasi (lebih banyak worker/container)Trafik tinggi; butuh shared state (DB, cache)

Untuk Flask, titik awal yang tepat: lebih banyak worker Gunicorn (scaling horizontal di satu mesin), lalu tambah mesin/container dengan load balancer saat satu mesin penuh. Kedua arah membutuhkan pemahaman worker model — topik pertama kita.

Worker Gunicorn: Sync vs Gevent

Dari episode 22, tipe worker menentukan cara aplikasi memproses request paralel:

Worker sync vs gthread vs gevent
gunicorn --workers 4 --bind 127.0.0.1:8000 "app:create_app()"
gunicorn --workers 2 --threads 8 --bind 127.0.0.1:8000 "app:create_app()"
gunicorn --worker-class gevent --workers 8 --bind 127.0.0.1:8000 "app:create_app()"
  • sync: satu worker = satu request pada satu waktu. Jika view menunggu I/O (DB, API eksternal), worker "menganggur" menunggu — kapasitas terbuang. Butuh banyak worker.
  • gthread: threads dalam worker menangani beberapa request bersamaan — cocok untuk I/O yang lambat tanpa overhead proses penuh.
  • gevent: green threads (cooperative scheduling) — ribuan koneksi "bersamaan" dalam satu worker untuk workload I/O-bound.

Pilih berdasarkan profil aplikasi:

Pedoman pemilihan worker
CPU-bound  (komputasi berat)  -> sync, jumlah worker = CPU cores
I/O-bound  (DB, jaringan)     -> gthread/gevent
Campuran                      -> gthread dengan threads sedang

Uji dengan load test (di bawah) untuk memutuskan — jangan menebak.

Note

Flask adalah framework sinkron — view biasa memblokir. Jika aplikasi kalian banyak menunggu API eksternal (integrasi payment, webhook), gevent memberi keuntungan besar tanpa menulis ulang kode. Namun pastikan library jaringan yang dipakai kompatibel dengan monkey-patching gevent — kode blocking lama justru akan melambat atau macet.

Connection Pooling Database

Setiap worker membuka koneksi database — membuka koneksi baru per request sangat mahal. Connection pooling menyimpan kumpulan koneksi yang dipakai ulang:

PythonPooling untuk PostgreSQL
class ProductionConfig(BaseConfig):
    SQLALCHEMY_ENGINE_OPTIONS = {
        "pool_size": 10,                # koneksi siap pakai
        "max_overflow": 20,             # tambahan saat ramai
        "pool_pre_ping": True,          # cek koneksi sebelum dipakai
        "pool_recycle": 3600,           # buang koneksi berumur > 1 jam
    }
  • pool_size: koneksi yang dijaga tetap terbuka.
  • pool_pre_ping: memeriksa koneksi sebelum query — menghindari error "connection lost" setelah DB restart.
  • pool_recycle: membuang koneksi yang umurnya melebihi timeout server (PostgreSQL default 1 jam).

Untuk SQLite, pooling hampir tidak relevan (file-based, single writer) — inilah alasan lain produksi memakai PostgreSQL (episode 10).

Caching Strategis

Dari episode 15, caching adalah senjata paling cepat mengurangi beban. Pola yang paling berdampak:

  1. Response cache untuk endpoint publik yang jarang berubah (kategori, harga, landing page).
  2. Query/memoize untuk hasil komputasi berat yang dipakai berulang.
  3. Redis sebagai shared cache — krusial saat scaling horizontal: cache harus dibagi antar instance, bukan per-proses (SimpleCache tidak akan cukup).
PythonCache strategis di production
class ProductionConfig(BaseConfig):
    CACHE_TYPE = "RedisCache"
    CACHE_REDIS_URL = os.environ.get("REDIS_URL")
    CACHE_DEFAULT_TIMEOUT = 300

Urutan pengoptimalan yang disarankan: (1) ukur, (2) cache yang paling murah & paling sering dipakai, (3) optimasi query, (4) baru sentuh worker. Biasanya 80% perbaikan datang dari 20% endpoint yang paling panas.

Load Testing: Ukur Sebelum Optimasi

Optimasi tanpa pengukuran adalah tebakan. Load testing mensimulasikan beban request untuk menemukan bottleneck nyata. Alat populer: hey, wrk, k6, locust. Contoh dengan hey:

Load test sederhana dengan hey
hey -n 10000 -c 100 http://127.0.0.1:8000/api/users
  • -n 10000 — total 10.000 request.
  • -c 100 — 100 request bersamaan (concurrency).

Output yang perlu diperhatikan:

Metrik yang dibaca
Total:        12.4 secs
Requests/sec: 806.45
Latency:      99.9%  ... 0.284 secs
  • Requests/sec — throughput.
  • Latency percentiles (p50, p99) — bagaimana pengalaman pengguna: p99 tinggi berarti sebagian pengguna menunggu lama.
  • Error rate — request yang gagal di bawah beban.

Metodologi yang benar: baseline dulu (tanpa optimasi), ubah satu variabel (misal worker type), ukur lagi, bandingkan. Satu perubahan per pengukuran.

Optimasi Query: Bottleneck Paling Umum

Worker dan cache tidak membantu jika database adalah sumbunya. Beberapa query anti-pattern:

PythonN+1 query - anti-pattern
# BERBAHAYA: 1 query untuk 100 posts + 100 query untuk author
for post in posts:
    print(post.author.name)      # trigger query per post
 
# AMAN: eager load sekali
from sqlalchemy.orm import selectinload
posts = db.session.scalars(
    db.select(Post).options(selectinload(Post.author))
).all()

N+1 query — satu query list + query per baris — adalah penyebab "API terasa lambat" paling umum. selectinload/joinedload (episode 10) menggabungkan jadi dua query. Daftar periksa cepat:

  • Gunakan pagination (episode 14) — jangan tarik seluruh tabel.
  • Tambahkan index pada kolom yang difilter/diurutkan (email, created_at).
  • Kurangi data yang dikirim: jangan serialize field yang tidak dipakai client.
  • Profil query dengan SQLAlchemy echo atau EXPLAIN untuk menemukan yang mahal.

Common Pitfalls Scaling & Performance

  • Optimasi tanpa pengukuran: ubah-worker-mengubah-worker tanpa load test = buang waktu.
  • Worker sync untuk I/O-heavy: worker menganggur menunggu — pilih gthread/gevent.
  • Tanpa connection pooling: koneksi baru per request — DB dibanjiri handshake.
  • Cache per-proses saat multi-instance: cache tidak kebagian antar worker — pakai Redis.
  • N+1 query: API lambat yang menyamar sebagai masalah infra.
  • Menaikkan worker tanpa batas: memori habis — worker butuh RAM per proses.

Penutup

Pada episode 24 ini, kalian telah belajar menaikkan kapasitas dan kecepatan aplikasi.

Inti yang harus dibawa pulang:

  • Scaling: horizontal (worker/instance) untuk trafik; vertikal untuk CPU/RAM.
  • Pilih worker Gunicorn sesuai profil: sync untuk CPU-bound, gthread/gevent untuk I/O-bound.
  • Connection pooling (pool_size, pool_pre_ping) untuk PostgreSQL.
  • Caching Redis shared antar instance; cache dulu yang paling panas.
  • Load testing: ukur baseline → ubah satu variabel → ukur lagi → bandingkan.
  • Selesaikan N+1 dan query mahal — database sering jadi bottleneck sesungguhnya.

Di episode 25 selanjutnya, kita melihat peta besar: ekosistem & tren modern 2026 — Flask 3.1.3 dengan Werkzeug 3.1, perbandingan mendalam Django vs FastAPI, dan kapan memilih Flask. Sampai jumpa di episode 25!

Belajar Flask - Scaling & Performance | Belajar Flask