8 menit

Uji beban Go sebelum merekrut engineer backend

Gunakan uji beban Go untuk memodelkan trafik realistis serta mengukur latensi p95, tekanan database, memori, dan kegagalan sebelum merekrut.

Uji beban Go sebelum merekrut engineer backend

Sepuluh ribu pengguna aktif bulanan bukan persyaratan kapasitas. Angka itu cocok untuk penagihan atau analitik. Layanan Go dapat melayani jauh lebih banyak pengguna jika permintaannya ringan dan tersebar sepanjang bulan, atau tumbang pada beberapa ratus pengguna jika setiap sesi memicu banyak kueri lambat, unggahan, dan panggilan ke layanan pihak ketiga.

Pertanyaan yang berguna adalah apakah backend hasil generasi memenuhi target layanan yang sudah ditetapkan di bawah puncak trafik yang diperkirakan, dengan ruang yang cukup untuk pertumbuhan dan satu kegagalan biasa. Anda dapat menjawabnya sebelum merekrut engineer backend, tetapi hanya jika uji beban menyerupai produk Anda dan merekam kegiatan API, runtime Go, dan PostgreSQL pada saat yang sama. Grafik hijau untuk latensi rata-rata hampir tidak membuktikan apa pun.

Inilah pengujian yang akan saya minta sebelum mengatakan kepada pendiri bahwa backend Go hasil generasi siap untuk 10.000 pengguna aktif bulanan. Pengujian ini menghasilkan keputusan lulus atau gagal yang dapat diulang, menunjukkan hambatan pertama, dan memisahkan masalah kapasitas dari masalah ketepatan fungsi.

Pengguna bulanan harus diubah menjadi permintaan puncak

Ubah perkiraan pengguna menjadi permintaan per detik sebelum memilih tingkat beban. Pengguna aktif bulanan menyembunyikan dua variabel yang menggerakkan backend: berapa banyak sesi yang datang dalam periode tersibuk dan berapa banyak pekerjaan yang dibuat oleh setiap sesi.

Mulailah dengan data pengamatan jika sudah ada beta privat. Hitung sesi dalam 15 menit tersibuk, permintaan per sesi, dan campuran rute. Jika belum ada trafik, tulis asumsi agar semua orang dapat mempertanyakannya. Misalnya, anggap 10.000 pengguna aktif membuat delapan sesi per bulan dan 15 permintaan API per sesi, sementara 20 persen trafik harian jatuh pada jam tersibuk. Hasilnya sekitar 8 permintaan per detik pada hari sibuk biasa. Peluncuran, notifikasi, tenggat penggajian, atau zona waktu yang sama dapat melipatgandakan puncak yang sebenarnya.

Jangan ubah hitungan itu menjadi ketepatan palsu. Gunakan untuk menentukan tiga tingkat pengujian:

  • Puncak yang diperkirakan: beban tersibuk yang saat ini Anda prediksi.
  • Puncak pertumbuhan: dua kali puncak yang diperkirakan, kecuali bisnis memiliki proyeksi yang lebih baik.
  • Tingkat tekanan: naikkan trafik sampai target layanan gagal atau sumber daya jenuh.

Pengujian puncak yang diperkirakan dan pertumbuhan menjawab apakah peluncuran punya ruang cadangan. Uji tekanan menunjukkan apa yang rusak lebih dulu dan bagaimana kegagalannya tampak bagi pengguna. Jawaban terakhir penting karena layanan yang cepat menolak pekerjaan berlebih lebih mudah dioperasikan daripada layanan yang menghabiskan semua koneksi database dan membuat rute lain macet.

Gunakan model beban terbuka untuk uji kapasitas. Model terbuka memulai permintaan pada laju kedatangan tetap meskipun permintaan sebelumnya melambat. Model tertutup dengan jumlah pengguna virtual tetap sering menyembunyikan keruntuhan: respons yang lebih lambat membuat pengguna tersebut mengirim lebih sedikit permintaan baru, sehingga beban yang ditawarkan justru turun ketika layanan kesulitan. Dokumentasi k6 dari Grafana membedakan model ini melalui executor berbasis laju kedatangan dan melaporkan dropped_iterations saat generator tidak dapat memulai pekerjaan terjadwal. Perlakukan iterasi yang terlewat sebagai kegagalan generator pengujian, bukan keberhasilan server.

Jalankan setiap tingkat stabil setidaknya 30 menit setelah pemanasan. Pengujian lima menit melewatkan pergantian koneksi, siklus pengumpulan sampah, penggusuran cache, pekerjaan latar, dan pertumbuhan memori bertahap. Tambahkan uji ketahanan dua jam pada puncak yang diperkirakan setelah pengujian singkat lulus.

Tuliskan konversinya dalam lembar kerja kecil dan tampilkan semua satuan. Pengguna bulanan dikali sesi per pengguna dan permintaan per sesi menghasilkan permintaan bulanan. Lakukan pembagian hanya setelah membagi trafik ke hari operasional dan jam tersibuk. Lalu tambahkan retry, pekerjaan latar, webhook, dan polling yang mungkin tidak dihitung analitik pengguna. Frontend yang melakukan polling setiap sepuluh detik dapat membuat lebih banyak pekerjaan API daripada klik yang membuka halaman.

Modelkan lonjakan secara terpisah dari puncak stabil. Login setelah notifikasi, selesainya impor, atau retry klien setelah gangguan singkat dapat memusatkan pekerjaan dalam satu menit. Tambahkan tahap yang cepat mencapai laju lonjakan, bertahan cukup lama untuk mengisi antrean, lalu kembali normal. Layanan harus pulih tanpa backlog yang terus tumbuh atau restart manual. Catat waktu pemulihan sebagai hasil. Sistem dapat lulus uji stabil tetapi tetap tidak aman jika lonjakan singkat membuat pool atau worker macet.

Jangan kalikan laju akhir dengan faktor keamanan sembarang lalu menyebutnya realistis. Kaitkan cadangan dengan ketidakpastian bisnis: kesalahan proyeksi, kampanye yang direncanakan, satu replika tidak tersedia, atau waktu untuk menambah kapasitas. Uji setiap asumsi yang akan Anda andalkan. Simpan lembar kerja bersama hasilnya, karena keberhasilan kehilangan makna ketika tidak ada yang ingat proyeksi dan asumsi lonjakan yang menghasilkan target.

Campuran trafik harus menyerupai sesi nyata

Uji beban realistis mempertahankan frekuensi rute, ukuran payload, autentikasi, distribusi data, jeda pengguna, dan perebutan penulisan. Memukul /health seratus kali per detik mengukur handler kesehatan, bukan aplikasinya.

Bangun campuran dari log akses jika memungkinkan. Kelompokkan rute berdasarkan tindakan bisnis, bukan URL mentah, karena /projects/123 dan /projects/456 memiliki bentuk yang sama. Campuran yang masuk akal untuk SaaS awal mungkin mengalokasikan 45 persen untuk daftar dan detail, 20 persen untuk pencarian, 15 persen untuk pembuatan atau pembaruan, 10 persen untuk login dan penyegaran token, serta 10 persen untuk ekspor atau pekerjaan berat lain. Angka Anda harus berasal dari alur produk, bukan dari contoh ini.

Gunakan banyak akun dan record pengujian. Menggunakan satu akun berulang kali dapat membuat cache panas yang tidak realistis, membuat pembaruan pada satu baris berjalan serial, atau memicu batas laju yang akan tersebar pada trafik sungguhan. Siapkan tenant kecil, sedang, dan besar. Sertakan record yang tidak ada, input tidak valid, dan kegagalan otorisasi karena jalur error dapat menanyakan database atau mengalokasikan body respons secara berbeda dari jalur sukses.

Tempatkan unggahan besar dan ekspor panjang dalam skenario tersendiri jika target layanannya berbeda. Tetap jalankan bersamaan dengan trafik normal. Jika tidak, pengujian melewatkan insiden yang benar-benar dirasakan pengguna: satu jenis ekspor menempati pool sementara halaman pengaturan sederhana menunggu di belakangnya.

Jangan gunakan mock untuk PostgreSQL, object storage, antrean, atau layanan keluar dalam uji kapasitas akhir. Mock berguna untuk memisahkan biaya handler, tetapi menghapus dependensi yang paling mungkin menetapkan kapasitas. Arahkan pengujian ke stack staging dengan ukuran instance, pengaturan database, indeks, batas koneksi, dan jalur jaringan yang sama dengan produksi. Data yang sudah dibersihkan dan berbentuk seperti produksi lebih baik daripada seribu baris seed identik.

Hindari pengujian melalui cache distribusi konten jika API biasanya tidak di-cache. Sebaliknya, pertahankan cache sungguhan di jalur jika produksi menggunakannya. Tujuannya bukan membuat backend tampak sibuk. Tujuannya mereproduksi pekerjaan yang benar-benar dibuat permintaan pengguna.

Pengujian k6 yang dapat dijalankan harus mengodekan kontrak

Simpan threshold dan tahap trafik dalam version control agar hasil pengujian tidak menjadi tangkapan layar yang ditafsirkan setelahnya. k6 memperlakukan threshold sebagai kriteria lulus atau gagal dan keluar dengan kode bukan nol ketika gagal, sehingga hasilnya cocok untuk pemeriksaan rilis.

Kerangka berikut menjalankan sesi campuran berdasarkan laju kedatangan, memeriksa arti respons, dan menetapkan batas latensi terpisah untuk pembacaan biasa dan ekspor berat. Ganti rute, payload, dan target dengan nilai yang disepakati untuk produk Anda. Kodenya sengaja sederhana supaya pemeriksaan yang gagal dapat dipetakan ke tindakan pengguna.

import http from 'k6/http';
import { check, sleep } from 'k6';
import { Rate } from 'k6/metrics';

const businessErrors = new Rate('business_errors');

export const options = {
  scenarios: {
    expected_peak: {
      executor: 'ramping-arrival-rate',
      startRate: 5,
      timeUnit: '1s',
      preAllocatedVUs: 40,
      maxVUs: 200,
      stages: [
        { target: 10, duration: '5m' },
        { target: 10, duration: '30m' },
        { target: 20, duration: '10m' },
        { target: 20, duration: '30m' },
      ],
    },
  },
  thresholds: {
    'http_req_duration{name:project_list}': ['p(95)<300'],
    'http_req_duration{name:project_create}': ['p(95)<500'],
    'http_req_duration{name:export}': ['p(95)<2000'],
    http_req_failed: ['rate<0.01'],
    business_errors: ['rate<0.005'],
    dropped_iterations: ['count==0'],
  },
};

export function setup() {
  const response = http.post(`${__ENV.BASE_URL}/api/login`, JSON.stringify({
    email: __ENV.TEST_EMAIL,
    password: __ENV.TEST_PASSWORD,
  }), { headers: { 'Content-Type': 'application/json' } });
  check(response, { 'login succeeds': r => r.status === 200 });
  return { token: response.json('token') };
}

export default function (data) {
  const headers = { Authorization: `Bearer ${data.token}`, 'Content-Type': 'application/json' };
  const list = http.get(`${__ENV.BASE_URL}/api/projects?limit=25`, {
    headers,
    tags: { name: 'project_list' },
  });
  businessErrors.add(!check(list, {
    'list status is 200': r => r.status === 200,
    'list has items': r => Array.isArray(r.json('items')),
  }));

  if (Math.random() < 0.25) {
    const create = http.post(`${__ENV.BASE_URL}/api/projects`, JSON.stringify({
      name: `load-${__VU}-${__ITER}`,
    }), { headers, tags: { name: 'project_create' } });
    businessErrors.add(!check(create, { 'create status is 201': r => r.status === 201 }));
  }

  if (Math.random() < 0.03) {
    const runExport = http.post(`${__ENV.BASE_URL}/api/exports`, '{}', {
      headers,
      tags: { name: 'export' },
    });
    businessErrors.add(!check(runExport, { 'export accepted': r => r.status === 202 }));
  }

  sleep(Math.random() * 2 + 1);
}

Target contoh adalah titik awal, bukan janji universal. Tetapkan p95 per kelas rute berdasarkan penundaan yang dapat diterima pengguna dan persyaratan produk. Jangan menilai ekspor asinkron dan permintaan autocomplete dengan satu threshold global 300 ms.

Jalankan skrip dari mesin yang tidak meng-host aplikasi. Pastikan generator memiliki CPU cadangan dan tidak ada iterasi terlewat. Simpan commit yang tepat, konfigurasi environment, pengenal snapshot data, perintah, dan output mentah. Tanpanya, perbandingan berikutnya sebagian besar bergantung pada ingatan dan optimisme.

Kalibrasi generator sebelum mempercayai eksekusi panjang. Arahkan ke handler kecil tanpa pekerjaan database, naikkan laju di atas pengujian yang direncanakan, dan pastikan generator dapat mempertahankannya tanpa menghabiskan CPU, socket, atau jaringan sendiri. Saat k6 menambah pengguna virtual atau melaporkan iterasi terlewat, mesin beban mungkin menjadi batas. Sebarkan generasi ke beberapa mesin hanya jika satu mesin tidak mampu menawarkan pekerjaan yang diperlukan, lalu sinkronkan jam agar grafik server dan klien sejajar.

Berikan baseline yang tenang untuk setiap eksekusi. Hentikan migrasi, impor data, dan pekerjaan staging yang tidak terkait kecuali semuanya juga berjalan pada puncak produksi. Lalu jadwalkan pengujian kedua dengan pekerjaan latar sungguhan. Pasangan ini menunjukkan kapasitas API yang bersih dan kapasitas operasional yang akan diterima pengguna. Jika hanya eksekusi tenang yang lulus, rencana peluncuran bergantung pada kondisi staging yang palsu.

Buat skrip kedua untuk satu perjalanan pengguna dan jalankan dengan satu pengguna virtual sebelum menambah beban. Periksa setiap respons, record yang dibuat, dan tindakan pembersihan. Langkah ini menangkap token yang salah, pemeriksaan yang selalu mengembalikan true, atau data yang bertabrakan setelah iterasi pertama. Hasil kapasitas tidak berarti jika skrip menguji halaman error atau terus membaca objek cache yang sama.

p95 memerlukan konteks setiap rute

Gunakan latensi p95 karena rata-rata menyembunyikan sebagian kecil permintaan lambat, tetapi jangan membacanya sendiri. Pada p95 sebesar 800 ms, satu dari dua puluh permintaan memerlukan waktu setidaknya selama itu, sehingga halaman dengan beberapa permintaan dapat terus terasa lambat. Persentil juga tidak stabil pada rute dengan sedikit sampel, jadi tampilkan jumlah permintaan di sampingnya.

Catat p50, p95, p99, maksimum, throughput, dan tingkat error untuk setiap tindakan bisnis yang diberi nama. p50 menunjukkan perilaku normal, p95 menjadi gerbang layanan praktis, dan p99 menampilkan ekor tanpa membiarkan satu nilai maksimum mendominasi pembahasan. Pisahkan hasil menurut status. Respons 500 yang cepat tidak boleh memperindah cerita latensi.

Ukur durasi handler sisi server selain durasi yang dilihat klien. Selisihnya mencakup pembuatan koneksi, proxy, waktu jaringan, dan transfer respons. Jika p95 klien naik sementara handler tetap datar, cari di luar handler. Jika keduanya naik dan waktu tunggu database meningkat, permintaan mungkin mengantre untuk koneksi atau kueri.

Pisahkan hasil pemanasan dan kondisi stabil. Binary Go yang sudah di-deploy tidak melakukan kompilasi, tetapi cache dingin, koneksi baru, inisialisasi tertunda, dan autoscaling dapat mengubah menit pertama. Pengguna juga mengalami perilaku dingin, jadi simpan sebagai hasil tersendiri daripada menghapusnya.

Rata-rata tetap berguna untuk menghitung sumber daya. Total waktu database dibagi jumlah panggilan dapat menemukan kueri yang cukup lambat dan sangat sering. Nilai itu tidak dapat menggantikan target persentil. Bidang ini sering mencampur latensi dan kapasitas: latensi menjelaskan waktu pekerjaan yang selesai, sedangkan kapasitas menjelaskan jumlah pekerjaan yang dapat ditahan layanan tanpa antrean atau error yang tumbuh. Latensi bagus pada laju tercapai yang rendah tidak membuktikan kapasitas.

Tentukan kegagalan sebelum eksekusi. Saya akan menggagalkan uji peluncuran saat rute kritis melewatkan target p95, kegagalan HTTP tak terduga melampaui angka yang disepakati, pemeriksaan bisnis gagal, iterasi terjadwal terlewat, atau sumber daya terus jenuh. Lulus empat dari lima gerbang berarti eksekusi gagal dengan data diagnosis yang berguna.

Waktu tunggu koneksi mengungkap antrean database tersembunyi

Ubah persyaratan menjadi Go
Jelaskan batas latensi, timeout, dan database di chat saat Koder.ai membangun backend.

Instrumentasikan database/sql sebelum pengujian karena latensi aplikasi tidak memberi tahu apakah PostgreSQL lambat atau aplikasi menunggu untuk mengaksesnya. DB.Stats() dari Go melaporkan OpenConnections, InUse, Idle, WaitCount, dan WaitDuration bersama penghitung penutupan. Ekspor semuanya ke sistem metrik setiap beberapa detik.

func recordDBStats(ctx context.Context, db *sql.DB, g GaugeSet) {
    ticker := time.NewTicker(5 * time.Second)
    defer ticker.Stop()

    for {
        select {
        case <-ctx.Done():
            return
        case <-ticker.C:
            s := db.Stats()
            g.Set("db_open_connections", float64(s.OpenConnections))
            g.Set("db_in_use_connections", float64(s.InUse))
            g.Set("db_idle_connections", float64(s.Idle))
            g.Set("db_wait_count_total", float64(s.WaitCount))
            g.Set("db_wait_seconds_total", s.WaitDuration.Seconds())
        }
    }
}

Hitung perubahan penghitung kumulatif selama jendela stabil. Kenaikan WaitCount berarti permintaan harus menunggu koneksi bebas. Perubahan WaitDuration dibagi perubahan WaitCount memberi rata-rata tunggu pool selama interval. Gambarkan InUse terhadap MaxOpenConnections; garis datar pada batas disertai waktu tunggu yang naik menunjukkan kejenuhan pool.

Jangan langsung menaikkan SetMaxOpenConns sampai grafik tampak lebih baik. Perbaikan populer itu memindahkan antrean ke PostgreSQL dan dapat meningkatkan perebutan, penggunaan memori, dan latensi kueri. Cari dulu penyebab koneksi tetap sibuk: kueri lambat, transaksi yang terbuka selama panggilan jaringan, pembacaan baris satu per satu, atau panggilan Rows.Close() yang terlupa. Setelah itu, tentukan ukuran pool di dalam anggaran koneksi database untuk semua replika dan worker.

Dokumentasi Go menyatakan nilai SetMaxOpenConns yang tidak positif membuat pool tidak terbatas. Default itu berbahaya di produksi ketika beberapa replika dapat membuka koneksi bersamaan. Tetapkan batas eksplisit, atur perilaku idle dan umur koneksi secara sengaja, serta sisakan kapasitas untuk migrasi, administrasi, dan pekerjaan latar.

Pantau durasi transaksi secara terpisah. Handler dapat kembali dalam 200 ms sementara pembersihan tertunda atau transaksi bocor menahan koneksi jauh lebih lama. Metrik pool menunjukkan tekanan, tetapi trace atau waktu transaksi menunjukkan pemiliknya.

Bukti kueri lambat harus berasal dari PostgreSQL

Aktifkan pg_stat_statements di environment pengujian dan ambil snapshot sebelum serta sesudah setiap eksekusi. Dokumentasi PostgreSQL menjelaskannya sebagai pelacak statistik perencanaan dan eksekusi statement yang dinormalisasi. View-nya mencakup panggilan, baris, waktu total dan rata-rata, aktivitas block, serta block sementara. Bukti ini jauh lebih baik daripada menebak berdasarkan satu kueri yang muncul dalam sebuah trace.

Gunakan selisih antarsnapshot karena view tersebut kumulatif. Reset hanya di database pengujian yang terisolasi, karena reset menghapus bukti pekerjaan lain. Kueri berikut menemukan statement yang memakai waktu eksekusi terbesar selama jendela bersih:

SELECT
  queryid,
  calls,
  round(total_exec_time::numeric, 1) AS total_ms,
  round(mean_exec_time::numeric, 2) AS mean_ms,
  rows,
  shared_blks_read,
  temp_blks_written
FROM pg_stat_statements
WHERE calls > 20
ORDER BY total_exec_time DESC
LIMIT 20;

Waktu total menemukan kueri sering yang mendominasi pekerjaan database. Waktu rata-rata menemukan statement yang lambat secara individual. Keduanya tidak memberikan p95 karena pg_stat_statements mengagregasi panggilan. Gunakan tracing atau histogram durasi jika latensi ekor sebuah statement penting. Ini perbedaan lain yang sering kabur: kueri lambat dapat berarti waktu rata-rata tinggi, ekor tinggi, atau biaya total besar karena terlalu sering dipanggil. Setiap penyebab memerlukan perbaikan berbeda.

Untuk statement teratas, jalankan EXPLAIN (ANALYZE, BUFFERS) dengan parameter aman dan representatif di luar uji beban yang diukur. ANALYZE mengeksekusi statement, jadi bungkus perubahan data dalam transaksi yang di-rollback atau periksa di salinan yang dapat dibuang. Cari perbedaan tajam antara estimasi dan baris aktual, loop berulang, sequential scan pada tabel besar yang selektif, sort yang tumpah ke disk, dan banyak shared block yang dibaca.

Kode hasil generasi sering membuat pola N+1 yang tampak baik dengan data seed: mengambil 25 proyek, lalu menjalankan satu kueri pemilik dan satu kueri hitung per proyek. Pada sepuluh permintaan per detik, endpoint itu saja dapat membuat lebih dari 500 statement database per detik sebelum rute lain berjalan. Perbaikannya mungkin join, WHERE id = ANY($1) secara batch, atau hitungan yang sudah dihitung. Menaikkan batas pool tidak menghapus pemborosan.

Catat juga waktu tunggu lock. Kueri yang cepat sendirian dapat macet di bawah pembaruan bersamaan pada tenant, akun, atau sequence yang sama. Saat latensi melonjak hanya pada skenario penulisan, periksa waktu tunggu aktif dan batas transaksi daripada langsung menambah indeks.

Memori harus stabil di bawah beban tetap

Jaga staging mirip produksi
Deployment dan hosting Koder.ai memungkinkan pengujian aplikasi di luar mesin pengembangan lokal.

Nilai memori dari bentuknya sepanjang waktu, bukan satu puncak. Proses Go yang naik saat pemanasan lalu berosilasi di sekitar tingkat stabil berbeda dari proses yang baseline pascapengumpulan sampahnya terus naik selama dua jam.

Catat resident memory proses, alokasi heap Go, objek heap, jumlah goroutine, frekuensi dan jeda pengumpulan sampah, serta laju alokasi. Memori container penting karena sistem operasi mematikan proses berdasarkan batasnya, bukan hanya heap Go. Bandingkan memori setelah pengumpulan sampah pada beban serupa. Langkah ini mengurangi pola gigi gergaji normal dan memperlihatkan retensi.

Ekspos metrik runtime Go standar atau endpoint profiling yang dilindungi di staging. Ambil profil heap di dekat awal dan akhir uji ketahanan, lalu bandingkan lokasi alokasi yang tertahan dengan go tool pprof. Ambil juga profil goroutine. Jumlah yang terus tumbuh dapat menunjukkan permintaan macet di channel, body respons yang tidak ditutup, atau pekerjaan latar yang dimulai tanpa pembatalan.

Jangan menetapkan GOMEMLIMIT tepat sama dengan batas container. Proses juga membutuhkan memori untuk stack goroutine, pemetaan executable, buffer driver database, dan alokasi di luar heap. Sisakan ruang dan buktikan dengan payload terbesar. Pengujian dengan JSON kecil hanya memberi sedikit informasi tentang endpoint yang membaca unggahan 20 MB ke memori.

Paksa kasus yang tidak nyaman: body permintaan maksimum, hasil kueri besar, klien yang membatalkan, timeout, dan ekspor berulang. Pastikan memori kembali setelah pekerjaan selesai. Pantau juga CPU karena pengumpulan sampah berat dapat menjaga memori di bawah batas sambil merusak latensi.

Syarat lulus yang praktis menggabungkan batas dan tren. Resident memory harus tetap aman di bawah batas deployment pada puncak pertumbuhan, kemudian baseline pascapengumpulan dan jumlah goroutine harus berhenti naik selama uji ketahanan. Tidak ada persentase aman universal. Pilih ruang sesuai perilaku restart platform, variasi trafik, dan kemampuan replika lain menyerap restart.

Kegagalan mencakup jawaban salah dan perilaku kelebihan beban

Jaga source Go tetap teruji
Koder.ai mengekspor backend hasil generasi agar skrip beban dan metrik dapat disimpan bersamanya.

Hitung kegagalan transport, status HTTP, timeout, panic, dan respons salah secara terpisah. http_req_failed menangkap permintaan HTTP gagal menurut callback k6, tetapi respons 200 dengan daftar kosong, tagihan ganda, atau record hilang tetaplah kegagalan. Itulah alasan contoh menghasilkan business_errors dari pemeriksaan isi.

Beri tag pada penolakan yang diharapkan seperti input tidak valid atau batas laju yang disengaja agar tidak mencemari tingkat error tak terduga. Lalu pastikan kontraknya: status benar, body terbatas, dan penolakan cepat. Sistem yang kelebihan beban tidak seharusnya menghabiskan 30 detik sebelum mengembalikan 503.

Periksa log server untuk pemulihan panic, error deadline konteks, keterlambatan perolehan koneksi, kegagalan serialisasi PostgreSQL, dan kueri yang dibatalkan. Kelompokkan error berdasarkan penyebab stabil, bukan seluruh pesan, agar identifier tidak membuat ribuan kategori. Simpan trace representatif untuk kejadian pertama dan ekor latensi tinggi.

Jalankan satu uji degradasi setelah uji kapasitas bersih. Kurangi koneksi database yang tersedia, tambahkan latensi terkontrol ke satu dependensi keluar, atau restart satu replika saat trafik terus berjalan. Lakukan hanya di environment terisolasi. Tujuannya memastikan timeout, pembatalan, dan pemeriksaan kesehatan membatasi kegagalan alih-alih membiarkan antrean menghabiskan semua sumber daya.

Konfigurasikan server HTTP secara eksplisit. Dokumentasi net/http Go menyatakan nilai nol atau negatif untuk ReadTimeout, WriteTimeout, dan IdleTimeout dapat berarti tanpa timeout, tergantung field. Layanan hasil generasi sering memanggil http.ListenAndServe dengan default tanpa membuat keputusan. http.Server khusus, deadline per permintaan, dan ukuran body terbatas mencegah klien lambat serta dependensi macet menahan sumber daya selamanya.

Periksa ketepatan fungsi setelah eksekusi. Hitung record yang dibuat, verifikasi idempotensi saat klien melakukan retry, pastikan pekerjaan latar selesai sekali, dan pastikan tidak ada state parsial tersisa setelah permintaan gagal. Uji beban telah menemukan lebih banyak bug pekerjaan ganda dalam proyek saya daripada code review yang terlihat pintar.

Keputusan rekrutmen berasal dari batas pertama

Anda dapat meluncur tanpa engineer backend ketika sistem berulang kali lulus pengujian puncak yang diperkirakan dan pertumbuhan, uji ketahanan mencapai baseline memori stabil, antrean database terkendali, dan tim dapat menjelaskan kegagalan stres pertama. Satu hasil hijau karena beruntung bukan bukti. Jalankan commit yang sama setidaknya tiga kali dan selidiki variasi besar.

Simpan catatan hasil ringkas untuk setiap eksekusi:

  • Commit dan environment, termasuk ukuran replika serta konfigurasi database.
  • Skala dataset, campuran trafik, laju kedatangan, dan durasi pengujian.
  • p95 dan p99 per rute, throughput tercapai, kegagalan bisnis, dan iterasi terlewat.
  • Puncak CPU dan memori, tren memori pascapengumpulan, dan tren goroutine.
  • Waktu tunggu pool, SQL teratas menurut waktu total, waktu tunggu lock, dan titik patah yang diamati.

Rekrut atau kontrak bantuan backend sebelum peluncuran jika tidak ada yang dapat menjelaskan waktu tunggu pool yang naik, memori tertahan, perebutan lock, atau penulisan yang tidak konsisten. Rekrut juga ketika satu-satunya orang yang dapat menjalankan pengujian tidak dapat mengubah kode hasil generasi dengan aman. Itu kesenjangan kepemilikan, bukan ambang permintaan per detik.

Pengujian gagal tidak otomatis membenarkan posisi penuh waktu. Indeks hilang, kueri N+1, atau ekspor tanpa batas mungkin hanya perbaikan terbatas. Kegagalan berulang pada desain transaksi, observability, pembatalan, dan perilaku deployment menunjukkan pekerjaan engineering berkelanjutan. Perbedaannya adalah apakah Anda menemukan satu cacat atau menemukan bahwa tidak ada yang memiliki perilaku sistem.

Koder.ai dapat membuat dan mengekspor backend Go, melakukan deployment, serta menyimpan snapshot untuk rollback, tetapi generasi tidak menghapus perencanaan kapasitas. Simpan skrip beban dan perubahan observability bersama source agar setiap perubahan backend penting harus memenuhi kontrak yang sama.

Jangan menjanjikan kepada bisnis bahwa 10.000 pengguna bulanan aman. Janjikan laju kedatangan terukur, campuran rute, target latensi, anggaran error, dan batas sumber daya. Saat produk berubah, ubah input tersebut dan jalankan pengujian lagi.

Pertanyaan umum

Bisakah backend Go menangani 10.000 pengguna aktif bulanan?

Sering kali bisa, tetapi pengguna bulanan tidak menjelaskan beban backend. Ubah proyeksi menjadi permintaan puncak per detik dan campuran rute, lalu uji terhadap batas latensi, error, database, dan memori yang jelas.

Berapa permintaan per detik untuk 10.000 pengguna bulanan?

Tidak ada konversi tetap. Anda memerlukan sesi per pengguna, permintaan per sesi, bagian trafik pada periode tersibuk, dan kejadian yang membuat pengguna datang bersamaan.

Berapa latensi p95 yang dapat diterima untuk API Go?

Tetapkan target berdasarkan tindakan pengguna, bukan bahasa atau framework. Pembacaan interaktif mungkin butuh beberapa ratus milidetik dan pekerjaan latar punya target lain, tetapi tim harus memilih sebelum melihat hasil.

Berapa lama uji beban backend harus berjalan?

Pertahankan tiap tingkat stabil setidaknya 30 menit setelah pemanasan, lalu jalankan uji ketahanan lebih panjang pada puncak yang diperkirakan. Pengujian pendek dapat melewatkan rotasi koneksi, retensi memori, pekerjaan latar, dan antrean yang tumbuh.

Haruskah memakai pengguna virtual atau laju kedatangan?

Gunakan model laju kedatangan untuk klaim kapasitas karena terus menawarkan pekerjaan saat layanan melambat. Model pengguna tetap dapat menurunkan laju permintaan saat lambat dan menyembunyikan pertumbuhan antrean.

Bagaimana mendeteksi pool koneksi Go yang habis?

Ekspor DB.Stats() dan pantau InUse, OpenConnections, WaitCount, serta WaitDuration. Penghitung tunggu yang naik saat koneksi terpakai berada di maksimum menunjukkan permintaan mengantre.

Haruskah pool SQL Go diperbesar saat permintaan menunggu?

Jangan sebelum mengetahui penyebab koneksi tetap sibuk dan kapasitas PostgreSQL. Pool yang lebih besar dapat memindahkan antrean ke database dan memperburuk perebutan.

Bagaimana menemukan kueri PostgreSQL lambat saat uji beban?

Ambil snapshot pg_stat_statements sebelum dan sesudah, lalu urutkan selisih kueri berdasarkan waktu total dan rata-rata. Gunakan trace atau histogram untuk ekor karena view agregat tidak memberi p95 per kueri.

Bagaimana mengetahui layanan Go mengalami kebocoran memori?

Jalankan uji ketahanan stabil dan bandingkan memori setelah pengumpulan sampah pada beban serupa. Baseline yang terus naik, terutama bersama objek heap atau goroutine, perlu perbandingan profil.

Kapan startup perlu merekrut engineer backend?

Rekrut saat kegagalan performa menunjukkan kebutuhan kepemilikan berkelanjutan atas desain database, observability, konkurensi, ketepatan, atau operasi. Satu indeks mungkin tidak butuh posisi penuh waktu, tetapi perilaku yang tak terjelaskan di bawah beban membutuhkannya.

Related posts