Systematyczne podejście do testów obciążeniowych: od błędów z tokenami do stabilnych metryk
Backend działający jako pośrednik między frontendem a zewnętrznym serwisem przechowywał token dostępu w pamięci podręcznej opcache. Pod obciążeniem kilka równoległych zapytań jednocześnie wykrywało wygasły token i próbowało go odświeżyć. Stary token nadpisywał nowy w pamięci podręcznej, co prowadziło do błędów 403.
Rozwiązanie: dodaliśmy blokadę przy odświeżaniu tokena i przenieśliśmy pamięć podręczną do klastrowego memcached dla jednolitego stanu między procesami. Test w Postmanie z równoległymi zapytaniami potwierdził wyeliminowanie wyścigu.
Metryki przed: czas odpowiedzi rósł wykładniczo, wskaźnik sukcesu spadał poniżej 90% przy 300 RPS. Po: wskaźnik sukcesu 100%, błędy 5xx <0,05%, średni czas 99-104 ms przy 226 RPS.
Przygotowanie backendu: checklista optymalizacji
Przed testami obciążeniowymi (TO) programista backendu przechodzi przez systematyczną checklistę. To minimalizuje wąskie gardła na poziomie kodu i infrastruktury.
Optymalizacja bazy danych
- Indeksowanie wszystkich pól w WHERE i JOIN.
- Analiza EXPLAIN dla wolnych zapytań.
- Replikacja master-slave, connection pooling (pgbouncer lub odpowiednik).
Pamięć podręczna
- Memcached dla powtarzających się danych.
- Pamięć podręczna odpowiedzi HTTP w Varnish lub nginx.
- Pamięć podręczna szablonów Twig.
Asynchroniczność
- Kolejki RabbitMQ lub Redis dla zadań w tle.
- Przeniesienie emaili, generacji PDF do background jobs.
Kod PHP
- Eliminacja zapytań N+1.
- Optymalizacja pętli, unikanie głębokiej rekurencji.
- Zwolnienie pamięci unset() dla dużych obiektów.
Infrastruktura
- php-fpm: pm.max_children > 100, pm.max_requests = 500.
- MinIO dla przechowywania plików.
- HPA w Kubernetes według CPU/RAM.
- Rate limiter (nginx lub Traefik) do ograniczania.
Frontend: redukcja przychodzącego obciążenia
Frontend generuje główny ruch. Optymalizacje tutaj redukują RPS na backendzie o 30-50%.
- Zasoby statyczne: pamięć podręczna nginx dla JS/CSS/obrazów.
- Pamięć podręczna API po stronie klienta: localStorage lub memcached dla rzadko zmieniających się danych (katalogi, konfiguracje).
- Paginacja: limit/offset, infinite scroll bez pełnego pobierania.
- SSG/ISR: Next.js do statycznego renderowania stron.
- SSE/Webhooks: zastąpienie polling przez powiadomienia push.
Przepływ testów obciążeniowych
TO są obowiązkowe dla funkcji zmieniających logikę serwerową, pamięć podręczną lub bazę danych. Przeprowadzane są na środowisku przedprodukcyjnym/produkcyjnym w nocy przy minimalnym ruchu użytkowników.
- Planowanie (na tydzień przed): lider + PM + DevOps oceniają docelowy RPS (N zapytań/s).
- Rola DevOps: konfiguracja podów (CPU/RAM), HPA, monitoring w czasie rzeczywistym.
- Uruchomienie: k6 lub odpowiednik, scenariusze według endpointów API.
- Monitoring: Grafana (RPS, opóźnienie, wskaźnik błędów), Graylog dla logów.
- Analiza: do punktu degradacji, naprawa wąskiego gardła, powtórzenie.
Sukces: wskaźnik sukcesu >99%, opóźnienie <150 ms przy docelowym RPS +20% zapasu.
Co jest ważne
- Wyścigi procesów (tokeny, sesje) wykrywane są tylko pod równoległym obciążeniem.
- Klastrowa pamięć podręczna (memcached) zamiast lokalnego opcache zapobiega race conditions.
- TO na środowisku podobnym do produkcyjnego: replikacja bazy danych, zewnętrzne serwisy.
- DevOps w cyklu: dynamiczna korekta zasobów podczas testu.
- Optymalizacje frontendu redukują obciążenie skuteczniej niż poprawki backendu.
Metryki sukcesu i benchmarki
Przykład z Grafany po poprawkach:
| Metryka | Średnia | Szczyt | Cel |
|---------|---------|-----|------|
| Wskaźnik sukcesu | 100% | 100% | >99% |
| Błędy 5xx | 0,017% | 0,042% | <0,1% |
| RPS | 226 | 321 | 300+ |
| Opóźnienie (ms) | 99-104 | 123 | <150 |
Stabilizacja na 226 RPS bez degradacji. Punkt awarii: ~450 RPS (zapas 100%).
Rekomendacje dla middle/senior
- Integrujcie TO w CI/CD: testy automatyczne przy mergu.
- Monitorujcie p99 latency, nie tylko średnią.
- Testujcie worst-case: 80% ruchu na jeden endpoint.
- Logujcie request ID do śledzenia (Jaeger/OpenTelemetry).
To podejście wykrywa problemy na etapie rozwoju, redukując incydenty w produkcji.
— Editorial Team
Brak komentarzy.