Membedah scaling Rails: aplikasi stateless, shared cache store, read replicas, worker terpisah, observability, serta praktik load test dan tuning menuju high availability

Setelah di episode 23 aplikasi kalian live di VPS, episode 24 membahas apa yang terjadi ketika trafik tumbuh: scaling. Satu server yang cukup untuk 100 user akan tercekik oleh 10 ribu. Pertanyaannya bukan apakah perlu scale, melainkan bagaimana melakukannya tanpa menulis ulang aplikasi.
Mengapa episode ini penting? Karena scaling Rails mengikuti pola yang sangat terprediksi — stateless app, shared cache, read replicas, worker terpisah, dan observability. Memahami pola ini sejak awal mencegah rewrite di tengah jalan dan membuat high availability (app tetap hidup saat satu komponen mati) menjadi desain, bukan keberuntungan.
Untuk menjalankan banyak instance app di belakang load balancer, aplikasi harus stateless — tidak menyimpan state di memori proses:
| State | Lokasi yang Benar |
|---|---|
| Session user | Cookie (default Rails) atau store shared |
| Cache | Redis / Solid Cache (shared store) |
| File upload | Volume persisten / S3 |
| Background jobs | Database queue (Solid Queue) |
| Realtime | Database pub/sub (Solid Cable) |
Jika app menyimpan data di memori proses (misal $global_state atau ActiveSupport::Cache::MemoryStore), instance yang berbeda melihat data berbeda — hasilnya inkonsisten. Rails 8 dengan Solid Stack sudah stateless-friendly secara default.
Cache harus shared antar instance (dari episode 12 & 22):
config.cache_store = :solid_cache_store
# atau
config.cache_store = :redis_cache_store, { url: "redis://.../1" }Konsekuensinya: fragment cache yang ditulis instance A bisa dibaca instance B. Cache in-memory per-instance akan menghasilkan hit rate rendah dan data stale.
Beban baca (SELECT) hampir selalu mendominasi. Read replicas memindahkan query baca ke replica database:
production:
primary:
url: <%= ENV["DATABASE_URL"] %>
pool: 10
primary_replica:
url: <%= ENV["REPLICA_DATABASE_URL"] %>
pool: 10
replica: trueAktifkan per-request (atau per blok) agar Active Record mengarahkan baca ke replica:
ActiveRecord::Base.connected_to(role: :reading) do
@posts = Post.published.limit(20)
endPola umum: sebagian besar request memakai replica untuk baca; transaksi tulis tetap di primary. Rails 8 juga punya automatic primary/replica routing berbasis per-thread jika diaktifkan.
Warning
Read replicas punya replication lag — data yang baru ditulis mungkin belum ada di replica beberapa milidetik. Untuk operasi read-after-write (misal redirect ke halaman yang baru dibuat), paksa baca ke primary atau gunakan routing transaksi. Jangan biarkan user melihat "404 setelah create".
Background jobs jangan berbagi resource dengan web process. Di Kamal (episode 23), proses terpisah dideklarasikan:
servers:
web:
- 203.0.113.10
- 203.0.113.11
workers:
hosts:
- 203.0.113.12
cmd: bin/rails solid_queue:startWeb di mesin 10-11, worker di mesin 12 — pekerjaan berat tidak memperlambat response. Dengan systemd (non-Kamal), daftarkan worker sebagai unit service tersendiri dengan restart otomatis.
Scaling butuh data. Observability = logs + metrics + traces:
| Pilar | Tool Rails | Fungsi |
|---|---|---|
| Logs | lograge (episode 17) | Riwayat request terstruktur |
| Metrics | solid_metrics / Prometheus | Request rate, error rate, latency |
| Traces | OpenTelemetry | Alur satu request lintas service |
Solid Metrics (ekosistem Solid Stack) menyimpan metrik di database yang sama — satu dashboard tanpa infra tambahan. Metrik yang wajib dipantau:
Jangan menebak kapasitas — ukur. Alur yang benar:
k6 run load-test.js
# atau
vegeta attack -targets=targets.txt -rate=100 -duration=30s | vegeta reportAlur pengukuran yang disarankan:
Contoh tuning yang umum: pool: 25 pada database config, timeout yang masuk akal, dan config.hosts yang tepat.
HA berarti app tetap melayani saat satu komponen mati. Pola yang menciptakannya:
/up) — load balancer mengeluarkan instance yang sakit.Ketika satu instance mati, user tidak menyadarinya — instance lain masih melayani dari cache shared dan database yang sama.
Episode 24 membekali kalian scaling Rails: aplikasi stateless sebagai fondasi horizontal scaling, shared cache store, read replicas untuk beban baca, worker terpisah untuk beban async, observability dengan logs/metrics/traces (Solid Metrics), dan load test untuk tuning berbasis data menuju high availability.
Inti yang harus dibawa pulang:
Di episode 25 selanjutnya kita akan membedah ekosistem & tren modern 2026 — Rails 8.1.3.1, Ruby 4.0 ZJIT, Hotwire, Solid Stack, Kamal, dan perbandingan dengan Laravel/Django/Node. Sampai jumpa di episode 25!