Kebutuhan dan Estimasi

Merumuskan pengguna, data, dan pertumbuhan disertai aritmetika yang dapat diperiksa ulang.

Daftar isi

Setelah produk dan lingkupnya ditetapkan, kebutuhan sistem diturunkan menjadi angka. Pengguna, permintaan, pertumbuhan penyimpanan, dan bandwidth aplikasi catatan dihitung dengan aritmetika yang seluruh potongannya dieksekusi, sehingga setiap angka pada desain dan pendalaman berikutnya berdiri di atas perhitungan yang dapat diperiksa ulang. Pola hitungannya mengikuti disiplin hitungan back-of-envelope (back-of-the-envelope calculation), yaitu asumsi dinyatakan lebih dahulu, dihitung dengan kode, kemudian dilaporkan pada orde besarnya.

Asumsi yang dinyatakan sebelum dihitung#

Seluruh angka pada topik ini diturunkan dari satu daftar asumsi yang ditulis lebih dahulu, sebagaimana disiplin estimasi yang menuntut asumsi terbuka. Daftar asumsinya adalah sebagai berikut.

  • 100.000 pengguna aktif harian, yaitu orde 10510^5.
  • 20 permintaan per pengguna per hari dari gabungan membaca, mencari, dan menulis.
  • Faktor puncak tiga, karena trafik puncak lazimnya dua sampai empat kali rata-rata.
  • Setengah catatan baru per pengguna aktif per hari, dengan teks rata-rata 2 KB.
  • Satu dari lima catatan baru membawa satu lampiran, dengan ukuran rata-rata 500 KB.
  • Delapan dari sepuluh permintaan berupa baca, dengan payload permintaan 1 KB dan payload respons 4 KB.
  • Setiap lampiran diunduh tiga kali setelah diunggah.

Setiap butir adalah asumsi pangkat sepuluh untuk keperluan desain, bukan hasil pengukuran produk nyata. Konsekuensinya diatur sejak awal, yaitu mengganti satu butir berarti menghitung ulang seluruh turunannya, dan hasil yang dilaporkan tetap dibulatkan ke orde besarnya. Angka desimal pada keluaran program adalah produk format, bukan sinyal ketepatan.

Permintaan dan puncak trafik#

Permintaan per hari diturunkan dari pengguna aktif dan frekuensinya, kemudian dibagi dengan jumlah detik sehari untuk memperoleh QPS (queries per second) rata-rata, lalu dikalikan faktor puncak untuk memperoleh beban yang harus ditangani kapasitas. Seluruh aritmetikanya berupa bilangan kecil, sehingga setiap barisnya dapat diperiksa ulang tanpa alat bantu. Berkas berikut mengeksekusi hitungan tersebut.

python estimasi-pengguna.py
# Estimasi permintaan aplikasi catatan dari pengguna aktif harian.

PENGGUNA_AKTIF_HARIAN = 100_000    # 10^5 pengguna aktif per hari
PERMINTAAN_PER_PENGGUNA = 20       # baca, cari, dan tulis digabung
FAKTOR_PUNCAK = 3                  # trafik puncak dibanding rata-rata

permintaan_per_hari = PENGGUNA_AKTIF_HARIAN * PERMINTAAN_PER_PENGGUNA
qps_rata_rata = permintaan_per_hari / (24 * 60 * 60)
qps_puncak = qps_rata_rata * FAKTOR_PUNCAK

print(f"Permintaan per hari : {permintaan_per_hari:,}")
print(f"QPS rata-rata       : {qps_rata_rata:,.1f}")
print(f"QPS puncak          : {qps_puncak:,.1f}")
text Keluaran
Permintaan per hari : 2,000,000
QPS rata-rata       : 23.1
QPS puncak          : 69.4

Hasilnya dua juta permintaan per hari, sekitar 23 permintaan per detik pada kondisi rata-rata, dan sekitar 70 permintaan per detik pada puncak. Angka ini kecil dibandingkan dengan kapasitas satu server aplikasi yang biasanya melayani ratusan permintaan per detik, sehingga kebutuhan kapasitas diestimasi satu sampai dua replika. Meskipun begitu, layanan aplikasi tetap dirancang stateless, karena tujuan penskalaan bukan hanya memenuhi beban hari ini, melainkan memastikan penambahan replika tidak menyentuh kode ketika pengguna bertumbuh. Angka rata-rata dan puncaknya dipisahkan dengan sengaja, karena kapasitas yang dibangun mengikuti puncak, sedangkan pemakaian harian yang sebenarnya berada dekat rata-rata. Angka puncak 70 juga menjadi patokan saat menentukan jumlah replika dan pengaturan load balancer pada desain tingkat tinggi.

Pertumbuhan penyimpanan#

Penyimpanan dihitung terpisah antara teks catatan dan lampirannya, karena keduanya berbeda orde besar dan berbeda tempat penyimpanan yang tepat. Teks berukuran kilobyte disimpan pada basis data, sedangkan lampiran berukuran ratusan kilobyte lebih cocok untuk object storage yang dirancang untuk berkas yang ditulis sekali dan dibaca berulang. Berkas berikut memproyeksikan keduanya ke periode bulanan dan tahunan.

python estimasi-penyimpanan.py
# Pertumbuhan penyimpanan aplikasi catatan, dipisah antara teks dan lampiran.

CATATAN_BARU_PER_HARI = 50_000   # setengah catatan baru per pengguna aktif
UKURAN_TEKS_KB = 2               # rata-rata teks satu catatan
PANGSA_LAMPIRAN = 0.2            # satu dari lima catatan baru membawa lampiran
UKURAN_LAMPIRAN_KB = 500         # rata-rata ukuran satu lampiran

teks_per_hari = CATATAN_BARU_PER_HARI * UKURAN_TEKS_KB
lampiran_per_hari = CATATAN_BARU_PER_HARI * PANGSA_LAMPIRAN * UKURAN_LAMPIRAN_KB

print(f"Teks catatan per hari     : {teks_per_hari / 10**6:.2f} GB")
print(f"Lampiran per hari         : {lampiran_per_hari / 10**6:.2f} GB")
print(f"Rasio lampiran ke teks    : {lampiran_per_hari / teks_per_hari:.0f} kali")
for label, hari in (("bulan", 30), ("tahun", 365)):
    teks = teks_per_hari * hari / 10**6
    lampiran = lampiran_per_hari * hari / 10**6
    print(f"Per {label:<5}: teks {teks:>7.2f} GB, lampiran {lampiran:>8.2f} GB")
text Keluaran
Teks catatan per hari     : 0.10 GB
Lampiran per hari         : 5.00 GB
Rasio lampiran ke teks    : 50 kali
Per bulan: teks    3.00 GB, lampiran   150.00 GB
Per tahun: teks   36.50 GB, lampiran  1825.00 GB

Lampiran mendominasi dengan rasio 50 kali teksnya. Per bulan, teks hanya menambah 3 GB, sedangkan lampiran menambah 150 GB, dan per tahun angkanya 36,5 GB berbanding 1.825 GB. Pemisahan ini langsung menjadi keputusan arsitektur, yaitu basis data menampung teks dan metadata yang kecil serta transaksional, sedangkan lampiran ditulis ke object storage agar volume besar itu tidak membebani basis data. Jumlah catatan kumulatif juga perlu dihitung, yaitu 50.000 catatan baru per hari dikali 365 hari, sehingga sekitar 18 juta catatan tersimpan setelah satu tahun. Angka kumulatif inilah yang nantinya menentukan kelayakan indeks pencarian pada desain tingkat tinggi.

Bandwidth#

Bandwidth menutup daftar kebutuhan ini, yaitu volume data yang melintas per detik pada sisi masuk (ingress) dan sisi keluar (egress). Hitungan untuk API mengalikan QPS puncak dengan payload rata-rata, sedangkan lalu lintas lampiran dihitung dari unggahan harian beserta rasio unduhannya. Keduanya dihitung terpisah karena berbeda karakter, yaitu trafik API banyak tetapi kecil, sedangkan transfer lampiran sedikit tetapi besar.

python estimasi-bandwidth.py
# Bandwidth trafik puncak aplikasi catatan, terpisah antara API dan lampiran.

QPS_PUNCAK = 70                     # dibulatkan dari hasil estimasi pengguna
PANGSA_BACA = 0.8                   # delapan dari sepuluh permintaan berupa baca
UKURAN_PERMINTAAN_KB = 1            # rata-rata payload permintaan API
UKURAN_RESPONS_KB = 4               # rata-rata payload respons API
LAMPIRAN_UNGGAH_GB_PER_HARI = 5     # dari estimasi penyimpanan
RASIO_UNDUH_UNGGAH = 3              # setiap unggahan diunduh tiga kali

ingress_api = QPS_PUNCAK * UKURAN_PERMINTAAN_KB * 1_000 * 8
egress_api = QPS_PUNCAK * PANGSA_BACA * UKURAN_RESPONS_KB * 1_000 * 8
unggah_lampiran = LAMPIRAN_UNGGAH_GB_PER_HARI * 10**9 * 8 / (24 * 60 * 60)
unduh_lampiran = unggah_lampiran * RASIO_UNDUH_UNGGAH

print(f"Ingress API puncak   : {ingress_api / 10**6:.2f} Mbps")
print(f"Egress API puncak    : {egress_api / 10**6:.2f} Mbps")
print(f"Unggah lampiran      : {unggah_lampiran / 10**6:.2f} Mbps (rata-rata)")
print(f"Unduh lampiran       : {unduh_lampiran / 10**6:.2f} Mbps (rata-rata)")
text Keluaran
Ingress API puncak   : 0.56 Mbps
Egress API puncak    : 1.79 Mbps
Unggah lampiran      : 0.46 Mbps (rata-rata)
Unduh lampiran       : 1.39 Mbps (rata-rata)

Keempat angka itu kecil, di bawah dua megabit per detik untuk setiap arah. Kesimpulannya dapat dinyatakan tanpa berbelit, yaitu pada lingkup 100.000 pengguna aktif, bandwidth bukan kendala, sehingga tidak ada alasan membangun lapisan tambahan untuknya. Apabila lingkupnya bertumbuh sepuluh kali, keempat angka ikut naik sepuluh kali dan tetap jauh dari ambang yang mengganggu, sehingga kesimpulan ini tahan terhadap perubahan asumsi. Angka unduhan lampiran 1,39 Mbps justru sedikit melebihi egress API, dan pola inilah yang menentukan keputusan desain berikutnya, yaitu unggah dan unduh lampiran dikeluarkan dari jalur layanan aplikasi dan diarahkan langsung ke object storage melalui URL bertanda tangan (presigned URL). Dengan cara itu, payload besar tidak memakan waktu layanan aplikasi, dan kapasitas yang diperlukan layanan tetap kecil.

Kesalahan umum#

Tiga kesalahan berikut paling sering muncul saat kebutuhan proyek dihitung, dan ketiganya tidak terdeteksi oleh program sekalipun aritmetikanya benar, karena yang keliru adalah masukannya.

  1. Menghitung permintaan hanya dari operasi tulis. Padahal pada aplikasi catatan, pembacaan dan pencarian jauh lebih sering daripada penulisan, sehingga profil beban yang diestimasi terbalik dan keputusan cache ikut salah. Perbaikannya, pisahkan baca dan tulis sejak estimasi pertama, lalu gunakan rasio tersebut secara konsisten.
  2. Menggabungkan teks dan lampiran menjadi satu angka penyimpanan. Angka gabungan menyembunyikan fakta bahwa keduanya berbeda orde besar dan berbeda tempat penyimpanan yang tepat, sehingga keputusan arsitektur tidak terlihat. Perbaikannya, pisahkan keduanya sejak baris pertama kode estimasi.
  3. Melaporkan hasil desimal seolah-olah presisi. Hitungan yang dibangun dari asumsi pangkat sepuluh tidak memiliki presisi desimal, dan angka yang tampak teliti justru menyesatkan pembaca desain. Perbaikannya, laporkan orde besarnya dalam prosa, misalnya sekitar 70 permintaan per detik pada puncak, sedangkan angka desimal hanya menjadi format keluaran program.

Ringkasan#

  • Asumsi dinyatakan lebih dahulu, dihitung dengan kode, dan dilaporkan pada orde besarnya, sehingga setiap angka dapat diperiksa ulang.
  • 100.000 pengguna aktif menghasilkan dua juta permintaan per hari, sekitar 23 QPS rata-rata, dan sekitar 70 QPS puncak.
  • Penyimpanan tumbuh 3 GB teks dan 150 GB lampiran per bulan, sehingga lampiran diserahkan kepada object storage dan teks kepada basis data.
  • Bandwidth pada lingkup ini kecil dan bukan kendala, sedangkan kendala sebenarnya ada pada penyimpanan lampiran dan pencarian.

Referensi#