# Runbook: Uji Beban (Load Testing) SIPORA

## Batasan penting lingkungan build ini

Sesi pengembangan ini hanya punya akses ke dev server bawaan PHP (`php -S`), yang **menangani
satu request pada satu waktu (single-threaded)** — bukan model worker-concurrent seperti
PHP-FPM di belakang Nginx/Apache yang dipakai di deployment sungguhan. Karena itu:

- Angka hasil uji beban terhadap `php -S` **TIDAK mencerminkan kapasitas produksi**. Angka
  itu hanya membuktikan alat `tools/load-test.php` bekerja benar secara mekanis (mengirim
  request konkuren, mengukur latensi dengan benar, mendeteksi error) — bukan kapasitas
  sungguhan sistem.
- **Uji beban yang berarti harus dijalankan ulang terhadap deployment sungguhan** (shared
  hosting/VPS dengan PHP-FPM atau setara) sebelum pilot diluncurkan ke sekolah sungguhan.

## Alat: `tools/load-test.php`

Generator beban GET konkuren pakai `curl_multi` (bukan library eksternal), melaporkan
throughput, distribusi status HTTP, dan persentil latensi (p50/p95/p99).

```bash
php tools/load-test.php --base-url=https://sipora-produksi.contoh.sch.id \
    --path=/health --concurrency=20 --requests=500
```

**Batasan alat ini (belum mendukung, sadar batasannya)**: hanya request GET tanpa autentikasi.
Endpoint yang butuh sesi login (mis. dashboard, submit asesmen) tidak bisa diuji langsung
lewat alat ini di versi sekarang — perlu pengembangan lanjutan (mis. login sekali di awal,
pakai cookie jar yang sama untuk semua request virtual user) bila diperlukan uji beban pada
alur yang butuh autentikasi. `/health` dipakai sebagai default karena tanpa autentikasi,
tanpa rate-limit, dan tetap menyentuh database (`SELECT 1`) sehingga jadi proksi yang wajar
untuk "apakah aplikasi+database responsif di bawah beban".

### Hasil uji coba (pembuktian alat, BUKAN kapasitas produksi)

Dijalankan terhadap `php -S` lokal, endpoint `/health`:

```
concurrency=5,  requests=30  -> 30/30 sukses, p50=57ms,  p95=77ms,  throughput=81.8 req/s
concurrency=3,  requests=10  (endpoint 404 sengaja) -> alat benar mendeteksi 10 response
                                                        non-2xx dan keluar dengan exit code 1
```

## Prosedur uji beban terhadap deployment sungguhan

1. **Jangan pernah uji beban langsung ke database/environment yang dipakai sekolah
   sungguhan** — pakai environment staging yang terpisah dengan data sintetis, atau jadwalkan
   di luar jam pakai sekolah dengan pemberitahuan lebih dulu bila terpaksa memakai environment
   yang sama.
2. Mulai dari beban kecil (`--concurrency=5 --requests=50`) dan naikkan bertahap
   (10, 20, 50...) sambil memantau CPU/memori server & jumlah koneksi database — jangan
   langsung lompat ke beban besar.
3. Amati juga sisi server selama uji berjalan: `SHOW PROCESSLIST` di MySQL (koneksi yang
   menumpuk?), log PHP-FPM (worker habis / request di-queue?), dan `storage/logs/` aplikasi
   (error baru yang tidak muncul di kondisi normal?).
4. Target kapasitas ditentukan oleh proyeksi jumlah sekolah pilot × siswa aktif bersamaan —
   diskusikan dengan pemilik produk sebelum menentukan angka "cukup".

## Catatan kapasitas dari tinjauan kode (bukan hasil pengukuran — perlu diverifikasi lewat uji beban sungguhan)

Titik-titik berikut diketahui dari desain kode selama pembangunan aplikasi ini, dicatat
sebagai hal yang perlu diperhatikan / diuji secara spesifik saat uji beban sungguhan
dilakukan nanti, bukan klaim performa yang sudah diukur:

- **Kunci baris kuota** (`consume_quota_slot()`, `SELECT ... FOR UPDATE` pada
  `school_quotas`): dengan sengaja menyerialkan penulisan kuota per (sekolah, periode) untuk
  mencegah race condition (sudah dibuktikan benar lewat `QuotaLockTest`). Ini berarti
  penambahan siswa/impor secara SANGAT konkuren pada SATU sekolah+periode yang sama akan
  saling menunggu gilirannya — wajar untuk volume pemakaian sekolah biasa, tapi jadi titik
  pengujian yang jelas bila ada skenario impor massal bersamaan.
- **Mesin skor** (`compute_attempt_scoring()` di `app/modules/scoring/service.php`):
  melakukan query database per butir dan per dimensi (bukan query batch tunggal), jadi jumlah
  round-trip database per submit asesmen berbanding lurus dengan jumlah butir instrumen.
  Untuk instrumen dengan banyak butir dan volume submit bersamaan tinggi (mis. seluruh kelas
  submit di jam yang sama menjelang tenggat), ini titik yang paling relevan diuji beban
  secara spesifik — bila jadi bottleneck, arahnya adalah membatch query per-attempt, bukan
  mengubah logika skornya.
- **Agregasi dashboard/peta prioritas** (`aggregate_dashboard_from_attempts()`,
  `build_priority_map()`): pola query serupa (per dimensi/per attempt dalam loop), tapi
  frekuensi akses jauh lebih rendah (staf membuka dashboard sesekali, bukan tiap detik) —
  prioritas lebih rendah dibanding mesin skor untuk dioptimasi.
- **Tabel `auth_attempts` dan `audit_logs` tumbuh tanpa batas** — tidak ada job pembersihan/
  arsip otomatis saat ini. Bukan masalah performa jangka pendek, tapi perlu rencana retensi
  (mis. arsipkan `audit_logs` lebih tua dari N tahun ke penyimpanan dingin) sebelum berjalan
  produksi bertahun-tahun — catat sebagai item persiapan pilot jangka panjang, bukan blocker.
- **Sesi PHP berbasis file** (default): cukup untuk satu server aplikasi. Bila di masa depan
  di-scale ke lebih dari satu server aplikasi di belakang load balancer, perlu sesi
  ter-share (mis. session handler berbasis database/Redis) atau sticky session — tidak
  relevan untuk pilot skala kecil di satu server, dicatat untuk perencanaan jangka panjang.

## Kriteria kesiapan (isi setelah uji beban sungguhan dijalankan)

- [ ] Uji beban dijalankan terhadap deployment sungguhan (bukan `php -S`), bukan hanya lokal.
- [ ] Endpoint kritis (login, dashboard, submit asesmen) diuji — perlu perluasan alat untuk
      mendukung request berautentikasi (lihat batasan di atas) sebelum ini bisa dicentang.
- [ ] Target konkurensi (jumlah siswa/staf aktif bersamaan yang diproyeksikan untuk pilot)
      ditentukan bersama pemilik produk dan dijadikan angka `--concurrency`/`--requests` uji.
- [ ] Tidak ada error 5xx maupun koneksi database yang menumpuk pada beban target.
- [ ] Hasil (latensi p95, throughput) didokumentasikan dan dibandingkan dengan ekspektasi
      pemilik produk sebelum lampu hijau pilot.
