API Yük Testi¶
Bu sayfa Sword API (Gezen Backend) için yapılan yük testi koşularını kaydeder. Her koşudan sonra sonuç tablosuna yeni bir satır ekleyin.
Hızlı Menü¶
- Özet ve Ana Bulgular
- Eşzamanlılık Sonuçları Tablosu
- Payload Sonuçları Tablosu
- Detaylı Bulgular ve Darboğazlar
- Çalıştırma Kılavuzu
- Sözlük — Terimler ve Metrikler
Özet ve Ana Bulgular¶
1. Pratik Eşzamanlılık Sınırı: Mevcut tek işlem (proses) kurulumumuz 50 eşzamanlı sanal kullanıcıya (VU) kadar sorunsuz çalışmaktadır. 50 VU'nun ötesinde gecikme süresi önemli ölçüde artmakta ve 200 VU'da sistem veritabanı bağlantı havuzunun tükenmesi nedeniyle kısmi çöküş yaşamaktadır.
2. Yavaşlık vs. Çöküş (Önemli Bir Ayrım): API'miz üzerindeki yüksek yük, uygulamanın tamamen çökmesinden ziyade aşırı yavaşlamaya (şifre doğrulama ve veritabanı bağlantı kuyruğuna takılma) neden olur. 100 VU'da bile hata oranı %0 kalmakta, ancak gecikme süresi ~2.5 saniye p95 değerine yükselmektedir (2 saniyelik hedefimizi aşmaktadır).
3. Eşzamanlılık vs. Payload: Eşzamanlılık testleri (çok kullanıcı, küçük istekler) ve Payload testleri (az kullanıcı, ağır yükleme/indirmeler) farklı mimari soruları yanıtlar. Bunları tek bir koşuda karıştırmayın.
4. Teknik Olmayan Görünüm: Bu sonuçların iş paydaşları için hazırlanmış, teknik terimlerden arındırılmış sade bir açıklaması için lütfen Kolaylaştırılmış Yük Testi Raporu belgesine bakın.
Yığın (stack)¶
| Katman | Teknoloji | Açıklama |
|---|---|---|
| Yük üreteci | k6 (Grafana) | Yüksek performanslı, geliştirici odaklı yük testi aracı |
| Eşzamanlılık betiği | scripts/load/api-load.js |
Tipik mobil/web uygulama trafiğini simüle eder |
| Payload betikleri | scripts/load/payload-upload.js, payload-read.js |
Ağır dosya yüklemelerini ve veri transferlerini simüle eder |
| Ortak yardımcılar | scripts/load/lib/common.js |
Ortak kimlik doğrulama ve istek yardımcıları |
| Yükleme fixture’ları | scripts/load/fixtures/ |
python3 scripts/load/generate_fixtures.py ile üretilir |
| API Çerçevesi | FastAPI (main.py) |
Yüksek performanslı Python web çerçevesi |
| ASGI sunucu | Uvicorn | Test edilen tek işlemli ASGI sunucusu |
| Veritabanı | PostgreSQL | İlişkisel veritabanı (SQLAlchemy ORM aracılığıyla) |
| Test edilen kimlik doğrulama | OAuth2 Password Flow | POST /api/auth/token (sonraki isteklerde JWT bearer) |
Eşzamanlılık testi vs payload testi¶
İki farklı soruya cevap verirler. Üretim öncesi ikisini de çalıştırın; ayrı betikler ve ayrı sonuç satırları kullanın — tek koşuda karıştırmak hataları yorumlamayı zorlaştırır.
| Eşzamanlılık (concurrency) | Payload (veri boyutu) | |
|---|---|---|
| Soru | Aynı anda kaç kullanıcı uygulamayı kullanabilir? | Her istek büyük veya maliyetli olduğunda ne olur? |
| Betik | api-load.js |
payload-upload.js, payload-read.js |
| Değişken | Sanal kullanıcı (VU), ramp hızı, bekleme süresi (think time) | İstek/yanıt boyutu, yükleme dosya boyutu, liste limit, PDF üretimi |
| Tipik VU | 20–200 | 1–5 (az kullanıcı, ağır işlemler) |
| Tipik istek boyutu | Küçük JSON (listeler, profil) | 1–12 MiB yükleme, büyük liste yanıtları, dosya indirme |
| Ana darboğazlar | Event loop kuyruğu, DB pool, bcrypt giriş | Disk I/O, bellek, serileştirme, Graphviz/PDF CPU |
| Ana metrikler | p95 gecikme, hata oranı, N VU’da ist/sn | http_req_sending, http_req_receiving, response_bytes, yükleme süresi |
| Önkoşul | Test kullanıcısı mevcut | Test kullanıcısı + en az bir fabrika/makine; yükleme için fixture üretilmiş |
Tanımlar için Sözlük — terimler ve metrikler bölümüne bakın.
Eşzamanlılık kapasiteyi yanıtlar: “50 kişi aynı anda fabrika ve görev listelerine bakabilir mi?”
Payload hacim ve ağırlığı yanıtlar: “3 kişi 12 MiB görsel yüklerken diğerleri limit=300 listeleri çekebilir mi?”
Sözlük — terimler ve metrikler¶
Tablolar veya k6 çıktısı tanıdık gelmiyorsa önce bu bölümü okuyun.
Temel kavramlar¶
| Terim | Anlamı |
|---|---|
| VU (Virtual User — sanal kullanıcı) | Betiği döngüde çalıştıran simüle edilmiş bir istemci. 50 VU ≈ aynı anda uygulamayı kullanan 50 kişi (her biri bir işlem yapar, sonra bekler). |
| Iterasyon | Bir VU için ana betiğin bir tam turu (bir HTTP isteği + think time). 20 VU ile 2.782 iterasyon, bu kullanıcıların koşu boyunca toplam 2.782 işlem yaptığı anlamına gelir. |
| Think time (düşünme süresi) | İşlemler arasındaki duraklama; gerçek kullanıcının ekranı okuması gibi. Eşzamanlılık testlerinde 1 sn; payload okuma testlerinde senaryoya göre 1–5 sn. Düşük think time = API’ye daha fazla baskı. |
| Ramp / stages (aşamalar) | VU sayısının zamanla nasıl artıp azaldığı. load-50 30 sn’de 50 VU’ya çıkar, 2 dk sabit tutar, sonra düşer — anlık sıçrama değildir. |
K6_SCENARIO |
Adlandırılmış test profili (örn. smoke, load-50, upload-12m). VU sayısı, süre ve geçti/kaldı eşiklerini belirler. |
| Setup | Testten önce bir kez çalışır (giriş, fabrika/makine oluşturma, 12 MiB görsel seed). Yük sayılmaz — ana döngü için veri hazırlar. |
Uç nokta yüzdeleri (trafik karışımı)¶
“GET /api/factories — %25” gibi tablolar her uç noktanın ne sıklıkla seçildiğini gösterir; başarı oranı veya sunucu yük payı değildir.
Her sanal kullanıcı rastgele bir sayı seçer ve bir uç noktaya istek gönderir:
| Aralık | Uç nokta | Pay |
|---|---|---|
| 0–10% | POST /api/auth/token |
%10 |
| 10–35% | GET /api/factories?limit=300 |
%25 |
| 35–60% | GET /api/machines?limit=100 |
%25 |
| 60–85% | GET /api/tasks?limit=100 |
%25 |
| 85–100% | GET /api/users/me |
%15 |
Uzun bir koşuda isteklerin kabaca %25’i fabrikalara gider — liste ekranlarının baskın olduğu mobil uygulamayı taklit eder. Payload betikleri aynı mantığı farklı ağırlıklarla kullanır (örn. read-fat’te %35 fabrika, %15 görsel indirme).
Gecikme ve verim¶
| Metrik | Ne ölçer | Nasıl okunur |
|---|---|---|
| p95 (95. persentil) | İstek süresi — isteklerin %95’i bu değerden hızlıydı; %5’i daha yavaştı. | Kayıtlarımızdaki ana gecikme sayısı. p95 1,04 sn = çoğu istek saniyenin altında, yavaş kuyruk ~1 sn’ye ulaştı. |
| p95 (tümü) | Koşudaki tüm HTTP istekleri için p95. | O senaryoda genel API yanıt hızı. |
| p95 auth / factories / … | Uç nokta bazında p95 (özel k6 trend’leri). | Hangi rota yavaş — örn. giriş (bcrypt) vs liste uç noktaları. |
| ist/sn (Req/s) | Saniyede tamamlanan HTTP isteği (verim). | Daha fazla VU ve kısa think time ile artar. “Saniyede kullanıcı” ile aynı değil — her kullanıcı işlemler arasında bekler. |
| Koşu süresi | k6 koşusunun duvar saati süresi (ramp dahil). | Senaryo adındaki “tutma” süresinden biraz uzundur. |
Hatalar, kontroller ve geçti/kaldı¶
| Metrik | Ne ölçer | Nasıl okunur |
|---|---|---|
| Hata oranı | Başarısız isteklerin payı (2xx dışı HTTP, zaman aşımı veya başarısız betik kontrolleri). | %0,00 = hata yok. %6,05 ≈ 16 istekten 1’i başarısız — çoğunlukla ağır yük altında zaman aşımı; her zaman HTTP 500 değildir. |
| Eşikler (thresholds) | Betikte tanımlı geçti/kaldı kuralları, örn. p95 < 2 sn ve hata < %1. |
k6 sonunda ✓ veya ✗ basar. Sonuç sütunumuz buna karşılık gelir. |
| Sonuç (Geçti / Kaldı) | Koşu eşikleri karşıladı mı? | Kaldı (gecikme) = istekler başarılı ama çok yavaş. Hatalı Kaldı = zaman aşımı veya HTTP hataları. |
Geçen kontroller (örn. 2783/2783) |
Betik doğrulamaları (status 2xx, login returns token vb.). |
Gecikme yüksekse tüm kontroller geçebilir ama eşikler yine kaldırabilir. |
| İstek zaman aşımı | k6 beklemeyi bıraktı (varsayılan 60 sn). | 5xx gövdesi olmadan hata olarak görünür — sunucu aşırı yüklendiğinde veya DB pool tükendiğinde yaygın. |
HTTP zamanlama dökümü (k6 varsayılanları)¶
Gövdelerin büyük olduğu payload koşularında faydalı:
| Metrik | Aşama |
|---|---|
http_req_sending |
İstek gövdesini sunucuya gönderme süresi (yükleme). |
http_req_waiting |
Sunucuyu bekleme süresi (DB sorguları, disk, PDF). |
http_req_receiving |
Yanıt gövdesini indirme süresi (büyük listeler, görseller). |
http_req_duration |
gönderme + bekleme + alma toplamı (p95 ile aynı temel). |
Payload’a özel terimler¶
| Terim | Anlamı |
|---|---|
| MiB | Mebibayt (1 MiB = 1.048.576 bayt). Yükleme fixture’ları 1 / 6 / 12 MiB; 12 MiB uygulamanın max yükleme boyutu. |
limit=300 |
Liste uç noktalarındaki sorgu parametresi — en fazla 300 satır döner. Büyük limit = daha büyük JSON yanıtı. |
response_bytes / upload_bytes |
İstek başına gönderilen/alınan baytları izleyen özel k6 trend’leri. |
| 413 | HTTP “Payload Too Large” — dosya 12 MiB sınırını aştı. |
Sonuç tablosu sütunları (eşzamanlılık kaydı)¶
| Sütun | Anlamı |
|---|---|
| Max VU | Koşu sırasındaki eşzamanlı sanal kullanıcı zirvesi. |
| Toplam istek | k6’nın tamamladığı tüm HTTP istekleri (setup girişi ve iterasyon çağrıları dahil). |
| Ortam / Sunucu / DB | API’nin nerede ve nasıl çalıştığı — farklı kurulumlar arasında sonuçlar not edilmeden karşılaştırılamaz. |
| Eşikler | O senaryonun geçme kriterleri (betikten kopyalanır). |
| Notlar | Serbest metin — zaman aşımları, yeniden başlatmalar, yapılandırma değişiklikleri, aykırı değerleri açıklayan her şey. |
Sonuç tablosu sütunları (payload kaydı)¶
| Sütun | Anlamı |
|---|---|
| Tür | upload veya read. |
| Payload | Ne ağırdı — dosya boyutu, liste limitleri veya indirme boyutu. |
| p95 (tümü) | Genel gecikme; localhost’ta yükleme p95’si loopback’te gerçek ağ olmadığı için yanıltıcı derecede düşük görünebilir. |
k6 terminal çıktısını okuma (hızlı harita)¶
Koşu sonunda k6 bir özet basar. Sık satırlar:
http_req_duration— genel gecikme istatistikleri;p(95)=…bizim p95 (tümü) olarak kaydettiğimiz değer.http_req_failed— başarısız isteklerin oranı (hata oranımız).iterations— tüm VU’larda toplam döngü sayısı.vus— anlık / max sanal kullanıcı.- Eşik adlarının yanındaki
✓/✗— o kural için geçti veya kaldı.
Eşzamanlılık testleri (api-load.js)¶
k6 betiği tipik mobil/web uygulama trafiğini simüle eder:
- Setup (bir kez):
K6_USERNAME/K6_PASSWORDile giriş yapılır ve JWT alınır. - Ana döngü (sanal kullanıcı başına): ağırlıklı rastgele bir uç nokta seçilir, kimlik doğrulamalı istek gönderilir, ardından 1 saniye beklenir (think time).
Uç nokta Yaklaşık pay = trafik karışımı (her rotanın ne sıklıkla vurulduğu), başarı oranı değil. Sözlük bölümüne bakın.
| Uç nokta | Yaklaşık pay |
|---|---|
POST /api/auth/token |
%10 |
GET /api/factories?limit=300 |
%25 |
GET /api/machines?limit=100 |
%25 |
GET /api/tasks?limit=100 |
%25 |
GET /api/users/me |
%15 |
Senaryolar¶
K6_SCENARIO ile ayarlanır:
| Senaryo | Profil | Varsayılan eşikler |
|---|---|---|
smoke |
5 VU, 30 sn | p95 < 3 sn, hata < %5 |
load (varsayılan) |
4 dk içinde 50 → 200 → 0 | p95 < 2 sn, hata < %1 |
load-20 |
sabit 20 VU (30 sn ramp, 2 dk tut, 30 sn düş) | p95 < 2 sn, hata < %1 |
load-50 |
sabit 50 VU (30 sn ramp, 2 dk tut, 30 sn düş) | p95 < 2 sn, hata < %1 |
load-100 |
sabit 100 VU (30 sn ramp, 2 dk tut, 30 sn düş) | p95 < 2 sn, hata < %1 |
stress |
7 dk içinde 100 → 500 → 0 | p95 < 5 sn, hata < %5 |
Nasıl çalıştırılır¶
k6 kurulumu¶
macOS (Homebrew)
brew install k6
Linux (Debian / Ubuntu)
curl -fsSL https://dl.k6.io/key.gpg | sudo gpg --dearmor -o /usr/share/keyrings/k6-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/k6-archive-keyring.gpg] https://dl.k6.io/deb stable main" | sudo tee /etc/apt/sources.list.d/k6.list
sudo apt-get update
sudo apt-get install k6
Linux (Fedora / CentOS)
sudo dnf install https://dl.k6.io/rpm/repo.rpm
sudo dnf install k6
Windows (winget)
winget install k6
Windows (Chocolatey)
choco install k6
Doğrulama: k6 version. Diğer seçenekler (Docker, tek dosya binary): k6 kurulum dokümanı.
API’yi başlatma¶
Bir terminalde, repo kökünden:
macOS / Linux (bash veya zsh)
cd /path/to/gezen-backend
source venv/bin/activate
uvicorn main:app --host 0.0.0.0 --port 8000
Windows (PowerShell)
cd C:\path\to\gezen-backend
.\venv\Scripts\Activate.ps1
uvicorn main:app --host 0.0.0.0 --port 8000
Değişkenleri ayarlama ve çalıştırma (smoke testi)¶
macOS / Linux (bash veya zsh)
export BASE_URL=http://127.0.0.1:8000
export K6_USERNAME=loadtest
export K6_PASSWORD=loadtest123
K6_SCENARIO=smoke k6 run scripts/load/api-load.js
Windows (PowerShell)
$env:BASE_URL = "http://127.0.0.1:8000"
$env:K6_USERNAME = "loadtest"
$env:K6_PASSWORD = "loadtest123"
$env:K6_SCENARIO = "smoke"
k6 run scripts/load/api-load.js
Windows (Komut İstemi)
set BASE_URL=http://127.0.0.1:8000
set K6_USERNAME=loadtest
set K6_PASSWORD=loadtest123
set K6_SCENARIO=smoke
k6 run scripts/load/api-load.js
Ayrı bir test kullanıcısı kullanın (örn. POST /api/users ile loadtest). Onay olmadan üretimde yüksek yük uygulamayın.
Aşağıdaki komut örnekleri bash / zsh sözdizimini kullanır (export, VAR=değer cmd). Windows’ta ortam değişkenleri ve senaryo seçimi için yukarıdaki PowerShell veya Komut İstemi kalıplarını kullanın.
Hızlı referans: scripts/load/README.md
Eşzamanlılık sonuç kaydı¶
Koşu başına bir satır doldurun. Yeni kayıt eklerken alttaki şablon satırını kopyalayın.
| # | Tarih | Senaryo | Max VU | Süre | Ortam | Base URL | Sunucu | DB | Test kullanıcı | Toplam istek | ist/sn | Hata oranı | p95 (tümü) | p95 auth | p95 factories | p95 machines | p95 tasks | p95 users/me | Eşikler | Sonuç | Notlar |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 1 | 2026-06-24 | smoke | 5 | ~31 sn | Yerel dev (Mac) | http://127.0.0.1:8000 |
Uvicorn, tek süreç | PostgreSQL | loadtest |
75 | 2,41 | %0,00 | 203 ms | 206 ms | 24 ms | 12 ms | 15 ms | 10 ms | Geçti (p95 < 3 sn, hata < %5) | Geçti | 5 eşzamanlı kullanıcı sorunsuz; 74 iterasyon, 76/76 kontrol geçti, 4xx/5xx yok |
| 2 | 2026-06-24 | load | 200 | ~4 dk 30 sn | Yerel dev (Mac) | http://127.0.0.1:8000 |
Uvicorn, tek süreç | PostgreSQL | loadtest |
5513 | 20,43 | %6,05 | 2,78 sn | 3,23 sn | 2,95 sn | 1,27 sn | 3,01 sn | 1,28 sn | Kaldı (p95 < 2 sn, hata < %1) | Kaldı | 4 dk içinde 50 → 200 → 0; 5509 iterasyon, 5180/5514 kontrol geçti; zirvede 334 istek zaman aşımı |
| 3 | 2026-06-24 | load-20 | 20 | ~3 dk 01 sn | Yerel dev (Mac) | http://127.0.0.1:8000 |
Uvicorn, tek süreç | PostgreSQL | loadtest |
2782 | 15,38 | %0,00 | 369 ms | 389 ms | 383 ms | 199 ms | 378 ms | 194 ms | Geçti (p95 < 2 sn, hata < %1) | Geçti | Sabit 20 VU; 2781 iterasyon, 2783/2783 kontrol geçti |
| 4 | 2026-06-24 | load-50 | 50 | ~3 dk 00 sn | Yerel dev (Mac) | http://127.0.0.1:8000 |
Uvicorn, tek süreç | PostgreSQL | loadtest |
5600 | 31,05 | %0,00 | 1,04 sn | 1,13 sn | 1,17 sn | 616 ms | 1,18 sn | 593 ms | Geçti (p95 < 2 sn, hata < %1) | Geçti | Sabit 50 VU; 5599 iterasyon, 5601/5601 kontrol geçti |
| 5 | 2026-06-24 | load-100 | 100 | ~3 dk 01 sn | Yerel dev (Mac) | http://127.0.0.1:8000 |
Uvicorn, tek süreç | PostgreSQL | loadtest |
7398 | 40,83 | %0,00 | 2,57 sn | 2,41 sn | 2,87 sn | 1,39 sn | 2,80 sn | 1,39 sn | Kaldı (p95 < 2 sn, hata < %1) | Kaldı | Sabit 100 VU; 7397 iterasyon, 7399/7399 kontrol geçti; HTTP hatası yok ama p95 2 sn üstü |
Koşu #1 özeti¶
- Sonuç: 5 sanal kullanıcı / 30 saniye hatasız tamamlandı; genel p95 203 ms (3 sn duman eşiğinin çok altında).
- En yavaş yol: giriş (~200 ms ort.) — parola hash’i beklenen davranış.
- Liste uç noktaları: fabrikalar, makineler, görevler ve
users/meyerel donanımda çoğunlukla < 25 ms p95. - Kullanılan komut:
export BASE_URL=http://127.0.0.1:8000
export K6_USERNAME=loadtest
export K6_PASSWORD=loadtest123
K6_SCENARIO=smoke k6 run scripts/load/api-load.js
Koşu #2 özeti¶
- Sonuç: Varsayılan yük profili (4 dk içinde 50 → 200 VU) eşikleri geçemedi — genel p95 2,78 sn (hedef < 2 sn), hata oranı %6,05 (hedef < %1).
- Verim: 1 sn think time ile 5513 istek, ~20,4 ist/sn; 5509 tamamlanan iterasyon.
- Hata türü: Çoğunlukla istek zaman aşımı (k6 varsayılan 60 sn) ~200 eşzamanlı VU’da tek süreç Uvicorn’da — 5xx yanıtı değil.
- En yavaş p95 yollar: auth 3,23 sn, tasks 3,01 sn, factories 2,95 sn; machines ve
users/medaha düşük (1,27–1,28 sn). - Kullanılan komut:
export BASE_URL=http://127.0.0.1:8000
export K6_USERNAME=loadtest
export K6_PASSWORD=loadtest123
K6_SCENARIO=load k6 run scripts/load/api-load.js
Koşu #3–#5 özeti (sabit yük: 20 / 50 / 100 VU)¶
Her seviyede kapasiteyi ölçmek için ayrı koşular (30 sn ramp → 2 dk sabit → 30 sn düşüş). Koşu #2 sonrası DB pool tükenmesi nedeniyle API yeniden başlatıldı.
| VU | Sonuç | p95 (tümü) | ist/sn | Hata |
|---|---|---|---|---|
| 20 | Geçti | 369 ms | 15,4 | %0 |
| 50 | Geçti | 1,04 sn | 31,1 | %0 |
| 100 | Kaldı (gecikme) | 2,57 sn | 40,8 | %0 |
- 20 VU: Rahat marj — tüm uç nokta p95 400 ms altında.
- 50 VU: Hâlâ eşikler içinde; gecikme 20 VU’ya göre kabaca 3× (p95 1,04 sn).
- 100 VU: Başarısız istek yok ama genel p95 2,57 sn 2 sn yük eşiğini aşıyor — sert hata olmadan bozulma.
- Kırılma noktası: Tek süreç Uvicorn + varsayılan SQLAlchemy pool (30 + 40 overflow) ile 50–100 VU arası.
- Kullanılan komutlar:
export BASE_URL=http://127.0.0.1:8000
export K6_USERNAME=loadtest
export K6_PASSWORD=loadtest123
K6_SCENARIO=load-20 k6 run scripts/load/api-load.js
K6_SCENARIO=load-50 k6 run scripts/load/api-load.js
K6_SCENARIO=load-100 k6 run scripts/load/api-load.js
Payload testleri (payload-upload.js, payload-read.js)¶
Fixture oluşturma (makine başına bir kez)¶
Yükleme testleri 1 MiB, 6 MiB ve 12 MiB ikili dosyalar gerektirir (uygulama limiti app/services/upload_storage.py içinde dosya başına 12 MiB):
python3 scripts/load/generate_fixtures.py
Windows’ta PATH’te python3 yoksa python scripts/load/generate_fixtures.py kullanın.
Önkoşullar¶
- Eşzamanlılık testlerinin gerektirdiği her şey, artı:
scripts/load/fixtures/altında üretilmiş fixture’lar- Test kullanıcısının fabrika/makinesi yoksa
setup()otomatik oluşturur (K6_FACTORY_ID/K6_MACHINE_IDile geçersiz kılınabilir)
Yükleme betiği (payload-upload.js)¶
POST /api/machines/{id}/images — multipart JPEG yüklemeleri.
K6_SCENARIO |
Profil | Dosya boyutu |
|---|---|---|
upload-smoke |
1 VU, 30 sn | 1 MiB (hızlı kablolama kontrolü) |
upload-1m |
2 VU, ~3 dk | 1 MiB |
upload-6m |
2 VU, ~3 dk | 6 MiB |
upload-12m |
1→2 VU, ~3 dk | 12 MiB (uygulama limiti) |
upload-mix |
3 VU, ~3 dk | rastgele 1 / 6 / 12 MiB |
Herhangi bir senaryoda dosya boyutunu geçersiz kılma: K6_UPLOAD_SIZE=6mb
export BASE_URL=http://127.0.0.1:8000
export K6_USERNAME=loadtest
export K6_PASSWORD=loadtest123
python3 scripts/load/generate_fixtures.py
K6_SCENARIO=upload-smoke k6 run scripts/load/payload-upload.js
Okuma / indirme betiği (payload-read.js)¶
Büyük giden veri yollarını test eder:
| Uç nokta | Pay | Notlar |
|---|---|---|
GET /api/factories?limit=300 |
~%35 | varsayılan limit: K6_FACTORY_LIMIT |
GET /api/machines?limit=100 |
~%25 | varsayılan: K6_MACHINE_LIMIT |
GET /api/tasks?limit=100 |
~%20 | varsayılan: K6_TASK_LIMIT |
GET /api/uploads/... (makine görseli) |
~%15 | yüklenen görseli indirir |
GET /api/factories/{id}/mindmap |
~%5 | yalnızca read-mindmap senaryosunda; Graphviz gerekir |
K6_SCENARIO |
Profil | Amaç |
|---|---|---|
read-smoke |
5 VU, 30 sn | hızlı büyük okuma kontrolü (seed varsa 12 MiB görsel indirme) |
read-fat |
5 VU, ~3 dk | sürekli büyük liste + indirme karışımı |
read-mindmap |
1→2 VU, ~1,5 dk | Graphviz PDF üretimi stresi |
Graphviz yüklü değilse: K6_SKIP_MINDMAP=1
K6_SCENARIO=read-smoke k6 run scripts/load/payload-read.js
K6_SCENARIO=read-fat k6 run scripts/load/payload-read.js
Payload koşularında izlenecekler¶
http_req_sending— yükleme bant genişliği / istemci tarafı gönderme süresihttp_req_receiving— listeler ve görseller için indirme bant genişliğihttp_req_waiting— sunucu DB, disk veya PDF işiresponse_bytesveupload_bytesözel trend’leri- Yükleme senaryolarından sonra
uploads/disk kullanımı - Dosya 12 MiB’i aşarsa 413 yanıtları
Payload sonuç kaydı¶
| # | Tarih | Tür | Senaryo | Max VU | Süre | Payload | Ortam | p95 (tümü) | Hata oranı | Sonuç | Notlar |
|---|---|---|---|---|---|---|---|---|---|---|---|
| P1 | 2026-06-24 | upload | upload-smoke | 1 | ~31 sn | 1 MiB JPEG × 10 | Yerel dev (Mac) | 111 ms | %0,00 | Geçti | Yalnızca kablolama kontrolü — gerçek payload testi değil |
| P2 | 2026-06-24 | read | read-smoke | 5 | ~31 sn | limit=300/100 + 1 MiB indirme | Yerel dev (Mac) | 36 ms | %0,00 | Geçti | Küçük DB’de hızlı okuma kontrolü |
| P3 | 2026-06-24 | upload | upload-6m | 2 | ~3 dk 05 sn | 6 MiB JPEG × 40 | Yerel dev (Mac) | 31 ms | %0,00 | Geçti | ~252 MB gönderildi; yükleme p95 ~31 ms |
| P4 | 2026-06-24 | upload | upload-12m | 2 | ~3 dk 01 sn | 12 MiB JPEG × 20 (uyg. max) | Yerel dev (Mac) | 46 ms | %0,00 | Geçti | İzin verilen max boyutta ~252 MB gönderildi |
| P5 | 2026-06-24 | upload | upload-mix | 3 | ~3 dk 05 sn | rastgele 1 / 6 / 12 MiB × 60 | Yerel dev (Mac) | 45 ms | %0,00 | Geçti | ~331 MB gönderildi; karışık boyutlar |
| P6 | 2026-06-24 | read | read-fat | 5 | ~3 dk 02 sn | limit=300/100 + 12 MiB indirme | Yerel dev (Mac) | 43 ms | %0,00 | Geçti | 382 iterasyon; ~643 MB alındı |
Payload boyut merdiveni (ne çalıştırılır)¶
| Senaryo | Dosya / yanıt boyutu | Rol |
|---|---|---|
upload-smoke / read-smoke |
1 MiB | Yalnızca hızlı kablolama kontrolü |
upload-6m |
6 MiB | Orta boy yükleme stresi |
upload-12m |
12 MiB | Uygulama limiti (MAX_UPLOAD_SIZE_BYTES) |
upload-mix |
rastgele 1 / 6 / 12 MiB | Gerçekçi karışık trafik |
read-fat |
Listeler + 12 MiB görsel indirme | Giden bant genişliği stresi |
Payload koşuları P1–P6 özeti¶
- P1–P2 (smoke): 1 MiB kasıtlı olarak küçük — betiklerin ve kimlik doğrulamanın çalıştığını doğrular.
- P3–P5 (yükleme merdiveni): 2–3 eşzamanlı VU ile 6 MiB ve 12 MiB (izin verilen max) — hepsi geçti, localhost’ta p95 31–46 ms. P3–P5’te toplam ~835 MB yüklendi.
- P6 (read-fat): 3 dk boyunca 5 VU, 12 MiB görsel indirmeleri — 643 MB alındı, p95 43 ms, %0 hata.
- Not: localhost loopback gerçek ağ gecikmesini gizler; WAN/mobil benzeri sayılar için staging’de tekrar çalıştırın.
- Kullanılan komutlar:
python3 scripts/load/generate_fixtures.py
export BASE_URL=http://127.0.0.1:8000
export K6_USERNAME=loadtest
export K6_PASSWORD=loadtest123
K6_SCENARIO=upload-6m k6 run scripts/load/payload-upload.js
K6_SCENARIO=upload-12m k6 run scripts/load/payload-upload.js
K6_SCENARIO=upload-mix k6 run scripts/load/payload-upload.js
K6_SCENARIO=read-fat k6 run scripts/load/payload-read.js
Payload şablonu (sonraki koşu için kopyala)¶
| # | Tarih | upload / read | senaryo | Max VU | Süre | Boyut / limitler | p95 | Hata | Sonuç | Notlar |
|---|---|---|---|---|---|---|---|---|---|---|
Bulgular — eşzamanlılık (yerel dev, 2026-06-24)¶
Beş eşzamanlılık koşusunun tamamı Mac geliştirme makinesinde loadtest kullanıcısı, istekler arası 1 sn think time ve tek süreç Uvicorn + PostgreSQL kullandı.
Kapasite özeti¶
| Sabit VU | Sonuç | p95 (tümü) | Hata oranı | Not |
|---|---|---|---|---|
| ≤20 | Geçti | 369 ms | %0 | Rahat marj |
| ~50 | Geçti | 1,04 sn | %0 | Yük eşiklerini karşılar (p95 < 2 sn, hata < %1) |
| ~100 | Kaldı (gecikme) | 2,57 sn | %0 | HTTP hatası yok; p95 2 sn hedefini aşıyor |
| ramp → 200 | Kaldı | 2,78 sn | %6,05 | 334 istek zaman aşımı; API yeniden başlatılana kadar yanıt vermedi |
Pratik yerel limit (mevcut kurulum): tek Uvicorn worker başına ~50 eşzamanlı kullanıcı planlayın. Bozulma 50–100 VU arasında başlar; 200+ zaman aşımı ve DB pool tükenmesine yol açar.
Sunucu çöker mi yoksa sadece çok mu yavaşlar?¶
Ölçülen koşular üç davranış ayırır. Yüksek yük çoğunlukla aşırı yavaşlık demektir; toplu çöküş değil. Gerçek bir kısmi çöküş (zaman aşımı, pool tükenmesi, yeniden başlatma gereksinimi) yalnızca en agresif eşzamanlılık rampasında görüldü.
| Davranış | Kullanıcının gördüğü | HTTP / sunucu | Ne zaman gördük |
|---|---|---|---|
| Sağlıklı | Hızlı yanıtlar | %0 hata, düşük p95 | ≤20 VU okuma mix; matrix'te 1 kullanıcı, çoğu route |
| Yavaş ama çalışıyor | Uzun beklemeler; uygulama kullanılabilir | HTTP hatası %0; p95 yükselir (en kötü route'larda 1–70+ sn) | ~50–100 VU okuma mix; 10–50 kullanıcıda tüm route'lara matrix yükü |
| Kısmi çöküş | Spinner zaman aşımı; yeni istekler takılabilir | Sıfır olmayan hata oranı (timeout); DB pool tükenir; yeniden başlatma gerekir | Varsayılan load ramp 200 VU (~%6 hata, 334 timeout) |
Önemli ayrımlar:
- Yavaş ≠ bozuk. Mobil okuma mix'inde 100 sabit VU'da tüm istekler hâlâ 2xx döndü — p95 ~2,57 sn, %0 hata. API ayaktaydı; yalnızca 2 sn hedefi için fazla yavaştı.
- 50 kullanıcıda matrix:
users.createp95 ~67 sn'ye çıktı; koşu yine %0 HTTP hatası kaydetti — istekler bcrypt, DB pool ve tek worker kuyruğunda bekledi; toplu hata yerine sonunda tamamlandı. - Çöküş dar ama gerçek. Koşu #2 (50→200 VU ramp)
QueuePool limit of size 30 overflow 40 reachedverdi; API yeniden başlatılana kadar iş kabul etmedi. Bu kapasite tükenmesi; rastgele 500 yağmuru değil. - İş kuralı hatası ≠ sunucu çöküşü. 30 kullanıcıda matrix'te
admin.users.deleteüzerinde bir 409 (silinen kullanıcıda hâlâ bildirim) — yük altında çakışma; altyapı arızası değil.
Gözlemlenen darboğazlar¶
- Tek süreç Uvicorn — tek event loop tüm eşzamanlı istekleri işler; yük altında auth (bcrypt) ve liste uç noktaları kuyruğa girer.
- SQLAlchemy bağlantı havuzu —
app/database.pyiçindepool_size=30,max_overflow=40. Koşu #2’deQueuePool limit of size 30 overflow 40 reachedve API yeniden başlatılana kadar istek kabul etmedi. - Auth uç noktası — parola hash’i yüksek VU sayılarında en yavaş yollardan biri (beklenen).
- Ağır liste uç noktaları —
factories?limit=300,tasks?limit=10050–100 VU’da en yüksek p95.
Önerilen sonraki adımlar¶
Daha ağır k6 koşularından önce:
- Uvicorn worker sayısını artırın — örn.
uvicorn main:app --host 0.0.0.0 --port 8000 --workers 4(pool_size × worker ≤ PostgreSQL max_connectionsolduğundan emin olun). - DB pool boyutunu gözden geçirin —
app/database.pyiçindekipool_size/max_overflowdeğerlerini worker sayısı ve Postgresmax_connectionsile hizalayın. - Sabit yükü tekrar test edin — yapılandırma değişikliklerinden sonra
K6_SCENARIO=load-100tekrar çalıştırın; hedef p95 < 2 sn, %0 hata.
İsteğe bağlı takip koşuları (Koşu #6+ veya Payload P7+ olarak kaydedin):
| Öncelik | Senaryo | Betik | Amaç |
|---|---|---|---|
| Ayarlama sonrası | load-100 |
api-load.js |
100 VU’nun eşikleri geçtiğini doğrula |
| Baseline | stress |
api-load.js |
7 dk içinde 100 → 500 → 0 |
| Payload | upload-smoke → upload-12m |
payload-upload.js |
Yükleme boyut merdiveni (1 / 6 / 12 MiB) |
| Payload | read-smoke → read-fat |
payload-read.js |
Büyük liste + görsel indirme |
| Payload | read-mindmap |
payload-read.js |
Fabrika PDF üretimi (Graphviz gerekir) |
| Prod öncesi | staging’de load-50 veya upload-mix |
her ikisi | Gerçekçi veri hacmi ve donanım |
Daha ağır koşularda ayrıca kaydedin (aşağıdaki kontrol listesine bakın): API sunucusunda CPU/bellek, PostgreSQL aktif bağlantıları ve liste payload’larının üretim satır sayılarını yansıtıp yansıtmadığı.
Şablon (sonraki koşu için kopyala)¶
| # | Tarih | YYYY-AA-GG | smoke / load / stress | ||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Daha ağır koşularda kaydedilecekler¶
load veya stress çalıştırırken Notlar sütununa şunları da ekleyin:
- API sunucusunda CPU / bellek
- PostgreSQL bağlantı sayısı veya pool tükenmesi
- p95’nin 2 sn’yi aştığı veya hataların başladığı ilk VU sayısı
- Veri hacminin (fabrika/makine/görev satırları) gerçekçi olup olmadığı
Genel backend kapasitesi (yavaş vs çöküş)¶
Bu bölüm repoda kayıtlı tüm performans test türlerini birleştirir. “Çok kullanıcı olunca ne olur?” sorusuna tek yerden cevap verir.
Üç test türü — üç soru¶
| Test | Soru | Araç | Çöküş vs yavaşlık |
|---|---|---|---|
| Eşzamanlılık yükü | Kaç kullanıcı aynı anda listelere bakabilir? | scripts/load/api-load.js |
~100 VU'ya kadar yavaş (%0 hata); 200 VU ramp'ta kısmi çöküş |
| Hacim / cardinality | Tablolar büyüyünce listeler (tek kullanıcı)? | scripts/volume/ |
Yalnızca yavaşlık — 1 istemcide T1'de %0 hata; sunucu çökmez |
| API matrix | Her route'a çok kullanıcı vurursa? | scripts/performance/ |
Çoğunlukla aşırı yavaş (r100/vu-50'de %0 HTTP hatası); SLO ihlali, kesinti değil |
Karıştırmayın: küçük veritabanında geçen 50 VU yük testi, üretim boyutunda veriyle tüm uygulamayı 50 kullanıcının kaldıracağı anlamına gelmez.
Birleşik kapasite tablosu (yerel dev, tek Uvicorn worker)¶
| Senaryo | Yaklaşık yük | Sunucu davranışı | Hatalar | Pratik sonuç |
|---|---|---|---|---|
| Tek kullanıcı, mütevazı veri | 1 istemci | Sağlıklı | %0 | Uygun |
| Tek kullanıcı, 6 aylık veri | 1 kullanıcı | Görevler 437 ms → 23 ms; fabrikalar 523 ms; makineler 351 ms | %0 hata | Görevler düzeltildi |
| ~20 kullanıcı, mobil okuma mix | 20 VU | Sağlıklı (~370 ms p95) | %0 | Rahat |
| ~50 kullanıcı, yalnızca okuma mix | 50 VU | Yavaş (~1 sn p95) | %0 | Çalışır; eşikte |
| ~100 kullanıcı, yalnızca okuma mix | 100 VU | Çok yavaş (~2,6 sn p95) | %0 HTTP hatası | Ayakta; kabul edilemez gecikme |
| 200 VU ramp, okuma mix | 200 VU tepe | Kısmi çöküş | ~%6 timeout | Pool tükendi; yeniden başlatma gerekti |
| 10+ kullanıcı, tam API yüzeyi (matrix) | 10–50 eşzamanlı route koşusu | Yazma/auth'ta aşırı yavaş | %0 HTTP (30 kullanıcıda bir 409) | Ağır iş akışları için prod'a hazır değil |
| Büyük yükleme / yağ okuma (payload) | 1–5 VU | Localhost'ta sağlıklı | %0 | Payload boyutu ana limit değil |
Trafik yönetimi için anlamı¶
- Okuma ağırlıklı iç kullanım (~20 kullanıcı, liste ekranları): backend genelde ayakta kalır ve yanıt verir; gecikme mevcut donanımda kabul edilebilir.
- ~50 kullanıcı yalnızca mobil yük betiğinin simüle ettiği işi yaparsa: yavaş ama çalışır — sabit
load-50'de ölçülen HTTP hata oranı %0. - ~50 kullanıcı gerçek yazma, kayıt, login, bildirim, teklif işlemi yaparsa: SLO'ları karşılamaz — matrix çok saniyeden dakikaya uzayan kuyruklar gösterir; istekler çoğunlukla tamamlanır, süreç çökmez.
- Agresif sıçramalar (200+ VU veya tükenmiş DB pool): sunucu kısmi çöküş moduna girebilir — istekler zaman aşımına uğrar, yeni bağlantılar takılır; Koşu #2'de manuel yeniden başlatma gerekti.
Ham matrix çıktıları: docs/performance-results/run-20260628-api-matrix/. Hacim ayrıntısı: Hacim Testi.
İlgili dokümanlar¶
- Analiz Raporu — proje değişiklik günlüğü ve kontrol listesi
- Hacim Testi — satır sayısı / cardinality (1 istemci)
- Yerel Kurulum — API’yi yerelde çalıştırma