System działa wolno. Który jego fragment jest winny?
Jak znaleźć opóźnienie między czujnikiem a panelem? Trzy identycznie wolne przepływy, zegary, ślady, kolejki i odtwarzalna analiza średniej oraz p99.

Operator widzi zmianę stanu czujnika dopiero po sekundzie. Zespół backendu pokazuje czas obsługi 30 ms. Bramka raportuje szybkie wysłanie wiadomości, a przeglądarka pobiera odpowiedź bez błędu. Każda z tych obserwacji może być prawdziwa. Nie wyjaśnia jednak, gdzie zniknęła pozostała część sekundy.
W systemie rozproszonym użytkownik czeka na wynik całej drogi, a zespoły zwykle mierzą własne fragmenty. Najtrudniejsze opóźnienia powstają często pomiędzy nimi: przed wysyłką, przed rozpoczęciem obsługi albo po zapisaniu danych, lecz przed zmianą widoku. Przyspieszenie krótkiego endpointu może wtedy poprawić jego wykres, nie rozwiązując problemu użytkownika.
Zbudujemy syntetyczny przepływ od czujnika przez bramkę, kolejkę i usługę do panelu. Trzy scenariusze będą miały ten sam czas końcowy i różne przyczyny. Następnie porównamy średnią z ogonem rozkładu oraz przygotujemy procedurę diagnostyczną. Czasy są jawnymi założeniami modelu z wirtualnym zegarem, nie pomiarami sieci, sprzętu ani wdrożenia BBest24.
Najpierw nazwijmy początek i koniec
W naszym zadaniu początek oznacza zdarzenie, które ma zostać odwzorowane w panelu. Koniec oznacza widoczną zmianę właściwego elementu interfejsu. Zapis w bazie danych nie kończy tego pomiaru. Otrzymanie potwierdzenia przez bramkę również go nie kończy. Są to przydatne punkty pośrednie, ale odpowiadają na inne pytania.
To rozróżnienie należy zapisać w wymaganiu. „API odpowiada w 100 ms” może być spełnione, gdy użytkownik czeka kilka sekund na okresowe odświeżenie. „Stan pojawia się w ciągu 500 ms od zdarzenia dla co najmniej 99% obsłużonych zdarzeń” jest bliższe doświadczeniu użytkownika, lecz nadal wymaga określenia obciążenia, warunków łączności i sposobu liczenia błędów.
Nie wolno wykluczyć nieudanych zdarzeń tylko dlatego, że nie mają czasu zakończenia. Jeśli część aktualizacji ginie, rozkład wyłącznie pomyślnych operacji może się poprawić, mimo że produkt działa gorzej. Obok czasu mierzymy więc liczbę rozpoczętych, zakończonych, odrzuconych i niezakończonych operacji. Przekroczenie czasu jest osobnym wynikiem, nie zerem ani brakującą komórką do pominięcia.
Dla fizycznego zdarzenia trzeba również określić, kiedy może je wykryć czujnik. Powolne próbkowanie tworzy oczekiwanie jeszcze przed pojawieniem się pierwszego komunikatu. Nasz model ujmuje początkowy etap w 20 ms; nie dowodzi, że dowolny sensor wykryje zmianę tak szybko. Gdy początek mierzymy dopiero w bramce, wynik należy nazwać opóźnieniem od bramki, bez sugerowania pomiaru od zdarzenia fizycznego.
Ta sama sekunda może powstać w trzech miejscach
Ścieżka bazowa składa się z sześciu kolejnych etapów. Przyjmujemy 20 ms na początek pomiaru, 30 ms w buforze bramki, 40 ms transmisji, 20 ms oczekiwania w kolejce, 30 ms pracy usługi oraz 60 ms do widocznej zmiany panelu. Suma wynosi 200 ms. Etapy są w tym modelu sekwencyjne i nie nakładają się.
W scenariuszu A dodajemy 800 ms do bufora bramki. W B dokładamy tyle samo do kolejki. W C dane trafiają do usługi normalnie, ale interfejs czeka dodatkowe 800 ms. Każdy przepływ trwa 1000 ms. W każdym sama praca usługi nadal zajmuje 30 ms.
W A wymiana serwera na szybszy nie usuwa oczekiwania przed wysyłką. Trzeba sprawdzić regułę grupowania wiadomości, harmonogram zadania i moment opróżniania bufora. W B ważne są wiek najstarszej wiadomości, tempo napływu i tempo rozpoczęcia obsługi. W C warto sprawdzić okres odpytywania, subskrypcję, odświeżenie stanu aplikacji i renderowanie.
Taki eksperyment służy także do odbioru obserwowalności. Jeżeli wiemy, gdzie dodano 800 ms, a raport wskazuje inny komponent, instrumentacja jest niekompletna albo jej interpretacja błędna. Nie należy stroić systemu na podstawie śladu, który nie potrafi odróżnić tych trzech kontrolowanych sytuacji.
Generator figur i kontrola rachunków korzystają z podanych wyżej czasów etapów i tego samego syntetycznego zbioru 1000 wyników. Nie używamy rzeczywistych opóźnień wykonywania programu jako rzekomego pomiaru infrastruktury.
Ślad potrzebuje tożsamości, nie tylko dat
Rozproszony ślad łączy operacje należące do jednego przepływu. OpenTelemetry opisuje span jako jednostkę pracy z początkiem, końcem i kontekstem. Powiązanie operacji pozwala odróżnić czas ich wykonywania od miejsca, w którym dopiero czekają na rozpoczęcie. Sam zbiór logów z podobną godziną bywa niewystarczający, szczególnie przy wielu urządzeniach.
W naszym przykładzie zdarzenie otrzymuje stabilny identyfikator logiczny. Każda próba wysłania i każda obsługa mogą mieć własny identyfikator wykonania. Numer próby i powód ponowienia pozostają widoczne. Dzięki temu trzy wysyłki tej samej zmiany nie są mylone z trzema niezależnymi zdarzeniami ani z jedną bardzo długą próbą.
W3C Trace Context standaryzuje propagowanie kontekstu przez nagłówki HTTP, w tym traceparent i tracestate. Nie oznacza to, że cały tor czujnikowy automatycznie przenosi je przez dowolny protokół. Na granicy bramki, brokera lub formatu binarnego trzeba sprawdzić sposób przenoszenia i odtwarzania powiązania w użytej implementacji.
Przy przetwarzaniu asynchronicznym albo operacji obejmującej wiele wiadomości przydatne są powiązania spanów, czyli links, opisane w dokumentacji OpenTelemetry. Nie każda zależność pasuje do prostego drzewa rodzic-dziecko. Ważne jest zachowanie relacji przyczynowej, a nie sztuczne rozciąganie jednego spana przez wielogodzinny okres offline tylko po to, by całość wyglądała jak pojedyncze żądanie.
Miejsca, które muszą zostawić ślad
Dla badanego przepływu potrzebujemy punktów obserwacji po obu stronach istotnych granic. Poniższa tabela nie wymaga konkretnego dostawcy telemetrii. Określa, co musimy umieć odróżnić.
W razie potrzeby przewiń tabelę poziomo.
| Punkt | Co zapisujemy | Jakie pytanie rozstrzyga |
|---|---|---|
| Zdarzenie i próbka | Identyfikator, czas lub licznik, identyfikator uruchomienia | Czy czekanie zaczęło się przed transmisją? |
| Bufor bramki | Wejście, gotowość do wysłania, numer próby | Czy wiadomość czekała na opróżnienie bufora? |
| Broker i konsument | Przyjęcie, udostępnienie, rozpoczęcie obsługi | Czy czas upłynął w kolejce? |
| Usługa | Początek, koniec, wynik, zależności | Ile trwała praca oraz oczekiwanie na zasób? |
| Panel | Otrzymanie wersji stanu i widoczna zmiana | Czy opóźnienie powstało po backendzie? |
Oznaczenie „wysłano” musi mieć jedno znaczenie. Może oznaczać przekazanie do lokalnej biblioteki, zapis do bufora systemowego albo potwierdzenie brokera. Jeśli dwa zespoły używają tego samego słowa dla różnych granic, odejmowanie ich czasów daje pozornie dokładne, lecz nieporównywalne wyniki.
Podobnie „wyświetlono” nie jest tym samym co wykonanie funkcji aktualizującej stan aplikacji. Jeżeli celem jest widoczny efekt, punkt końcowy należy powiązać z odpowiednim etapem interfejsu i sprawdzić w kontrolowanej próbie. Dla nieaktywnej karty przeglądarki trzeba ustalić osobny scenariusz, zamiast bezwarunkowo oczekiwać zachowania identycznego jak na aktywnym ekranie operatora.
Zegary potrafią stworzyć ujemną sieć
Załóżmy, że rzeczywista transmisja trwa 40 ms, ale zegar nadawcy wyprzedza zegar odbiorcy o 80 ms. Różnica zapisanych znaczników wyniesie minus 40 ms. Nie odkryliśmy ujemnego opóźnienia. Porównaliśmy dwa zegary z nieznanym przesunięciem.
Do czasu trwania operacji na jednym urządzeniu zwykle lepiej używać zegara monotonicznego, który nie zmienia wyniku po korekcie czasu kalendarzowego. Wyniku z jednego uruchomienia nie należy bezpośrednio odejmować od licznika po restarcie. Potrzebne są identyfikator uruchomienia i obsługa przepełnienia licznika, jeśli jego zakres jest ograniczony.
Między urządzeniami trzeba znać jakość synchronizacji. Gdy obliczona różnica wynosi 50 ms, a każdy z dwóch znaczników może mieć błąd do 20 ms, konserwatywny przedział rzeczywistego odstępu wynosi od 10 do 90 ms. Nie jest to automatycznie statystyczny przedział ufności; wynika z przyjętych granic błędu. Wynik „50,000 ms” nie staje się wiarygodniejszy przez większą liczbę cyfr.
Jeśli nie potrafimy uzasadnić synchronizacji, nadal możemy mierzyć lokalne czasy przebywania w buforach, kolejce i usłudze oraz ustalać kolejność na podstawie identyfikatorów. Nie należy jednak dopisywać brakującego czasu do sieci jako pewnej przyczyny. W raporcie pozostaje odcinek niezmierzony albo przedział, który wymaga dodatkowej instrumentacji.
Nie sumujmy wszystkiego, co ma długość
Suma etapów w naszym modelu działa, ponieważ są kolejne i rozłączne. Prawdziwe ślady mogą zawierać zagnieżdżone spany oraz równoległą pracę. Span usługi obejmujący 120 ms i jego podoperacja trwająca 80 ms nie dają 200 ms oczekiwania użytkownika. Te 80 ms może już znajdować się wewnątrz pierwszego zakresu.
Przykładowo po 10 ms przygotowania uruchamiamy równolegle dwie operacje trwające 80 i 120 ms, a następnie potrzebujemy 10 ms na złożenie wyniku. Jeżeli czekamy na obie, czas wynosi 10 + max(80, 120) + 10 = 140 ms, nie 220 ms. Interesuje nas ścieżka krytyczna, czyli zależności wyznaczające najwcześniejszy możliwy koniec.
To samo dotyczy percentyli. Suma p99 kilku usług nie jest ogólnie p99 całej operacji. Najwolniejsze próbki mogą dotyczyć różnych zdarzeń, a zależności między etapami mogą zmieniać ogon rozkładu. Percentyl od początku do końca wyznaczamy z czasów całych operacji, zachowując ich korelację, zamiast dodawać liczby z osobnych paneli.
Średnia 236 ms może ukryć pięć sekund
Przygotujmy 1000 syntetycznych czasów: 980 wyników po 200 ms, 15 po 1000 ms i 5 po 5000 ms. Średnia wynosi 236 000 / 1000 = 236 ms. Mediana i p95 wynoszą 200 ms. Jednak p99 wynosi 1000 ms, a p99,9 wynosi 5000 ms.
Stosujemy metodę nearest rank: dla percentyla p wybieramy pozycję ceil(p × n) po posortowaniu wyników, licząc pozycje od jednego. Dla p99 jest to pozycja 990. Inne narzędzia mogą interpolować lub przybliżać kwantyle, dlatego metoda i granice histogramu powinny być częścią raportu. Przy małej próbce wysoki percentyl opiera się na bardzo niewielu obserwacjach.
W tym zbiorze warunek 500 ms spełnia 980 z 1000 zdarzeń, czyli 98%. Cel 99% nie został osiągnięty, mimo że średnia jest znacznie poniżej limitu. Rozdział Google SRE o monitorowaniu wyjaśnia, dlaczego same średnie ukrywają wolny ogon i dlaczego warto zachowywać rozkład czasów. Nasze konkretne liczby są niezależnym przykładem, nie wynikiem opisanej tam usługi.
Wykres pokazuje kolejność po sortowaniu, nie chronologię zdarzeń. Nie mówi, czy pięć wolnych przypadków wystąpiło podczas jednej awarii, czy w pięciu różnych instalacjach. Do diagnozy potrzebujemy obu widoków: rozkładu pokazującego skalę problemu oraz kontekstu konkretnych wolnych operacji.
Dwie poprawki dają różne zwycięstwa
Pierwsza propozycja skraca pracę usługi o 20 ms dla każdego zdarzenia. Średnia spada z 236 do 216 ms, a p99 z 1000 do 980 ms. To rzeczywista poprawa w modelu, ale nadal nie spełnia celu 500 ms dla 99% zdarzeń. Najdłuższe przypadki trwają 4980 ms.
Druga propozycja usuwa dodatkowe 800 ms z piętnastu przypadków kolejki, pozostawiając pięć przypadków po 5000 ms. Średnia wynosi wtedy 224 ms, czyli jest wyższa niż po pierwszej optymalizacji. Mimo to p99 spada do 200 ms: 995 z 1000 operacji mieści się w 500 ms. Według przyjętego celu druga poprawka jest skuteczniejsza.
Nie oznacza to, że problem został całkowicie rozwiązany. p99,9 pozostaje równy 5000 ms. Jeśli te zdarzenia dotyczą jednej ważnej instalacji albo krytycznego działania, agregat dla całej floty może być zbyt łagodnym kryterium. Wybór metryki musi odzwierciedlać konsekwencję oczekiwania, a nie tylko dostarczać wygodny zielony wskaźnik.
W prawdziwym teście trzeba sprawdzić, czy poprawka nie zmienia równocześnie liczby odrzuconych operacji, obciążenia albo zużycia zasobów. Wyłączenie powolnej ścieżki może skrócić czas pomyślnych odpowiedzi i zwiększyć odsetek błędów. Dlatego porównujemy te same granice, podobne obciążenie i pełny zestaw wyników, nie wyłącznie nowy wykres p99.
Kolejka mówi o tempie, nie tylko o liczbie wiadomości
Długość kolejki bez kontekstu bywa myląca. Sto krótkich zadań może zniknąć szybciej niż jedno bardzo długie. Przydatne są wiek najstarszego elementu, rozkład czasu oczekiwania, liczba pracujących konsumentów i tempo napływu. W naszym B opóźnienie powstaje przed rozpoczęciem 30 ms pracy usługi, więc sam profil CPU tej pracy go nie wyjaśni.
Jeśli napływa 50 zdarzeń na sekundę, a każde przez błąd timeoutu jest wysyłane średnio dwa razy, odbiorca dostaje 100 prób na sekundę. Gdy każda z czterech warstw wykonuje własne ponowienie, model obciążenia może stać się znacznie bardziej złożony. Prosty przykład czterech prób na każde z tych stu żądań daje już 400 prób na sekundę. To założony scenariusz obciążenia, nie uniwersalny mnożnik retry.
Ponowienia potrzebują wspólnego budżetu czasu, ograniczenia liczby prób i rozróżnienia zdarzenia od dostarczenia. Krótszy timeout nie zawsze skraca oczekiwanie użytkownika; może wywołać dodatkową pracę, która przedłuża kolejkę. W diagnozie warto sprawdzić, czy wzrost opóźnień poprzedził wzrost retry, czy też ponowienia zaczęły się po zmianie konfiguracji i same pogorszyły sytuację.
Generator ruchu może ukryć przeciążenie
Wyobraźmy sobie test, który wysyła następne zdarzenie dopiero po zakończeniu poprzedniego. Gdy system zwalnia, generator również zwalnia. W efekcie zmniejsza obciążenie właśnie wtedy, gdy powinna narastać kolejka. Taki test może odpowiadać pojedynczemu operatorowi czekającemu na wynik, ale nie odtwarza floty czujników wysyłających niezależnie od szybkości serwera.
Jeżeli scenariusz zakłada 50 nowych zdarzeń na sekundę, trzeba zapisać zarówno planowany harmonogram, jak i rzeczywisty czas ich wygenerowania. Brak mocy generatora nie powinien wyglądać jak brak ruchu po stronie urządzeń. Opóźnienie startu i liczba pominiętych emisji należą do raportu testowego. Inaczej próba może utrzymywać niski czas odpowiedzi przez ciche niedostarczenie części pracy.
Równie ważny jest kształt ruchu. Równomierne 50 zdarzeń na sekundę nie jest tym samym co paczka 3000 zdarzeń raz na minutę, choć średnie tempo się zgadza. Drugi wariant może wystąpić, gdy urządzenia mają wspólny harmonogram albo wracają po przerwie w łączności. Warto sprawdzić oba, jeżeli mieszczą się w warunkach użytkowania. Nie należy deklarować gotowości na nagłe paczki na podstawie wyłącznie równomiernej próby.
Przed porównaniem wersji zachowujemy także stan początkowy: pustą lub zalegającą kolejkę, rozgrzane albo zimne połączenia oraz liczbę aktywnych konsumentów. Jeśli jedna wersja startuje z przygotowanym cache, a druga musi odbudować go od zera, różnica może nie wynikać z badanej poprawki. Takie warianty są wartościowe, ale powinny być nazwane osobno. Test wydajności ma odtworzyć określoną sytuację użytkownika, a nie wygenerować jedną liczbę pozbawioną warunków.
Próbkowanie śladów nie powinno ukrywać tego, czego szukamy
Pełne zapisywanie każdej operacji może być zbyt kosztowne. Decyzja o zachowaniu śladu na początku jest prosta, lecz nie wie jeszcze, czy operacja stanie się wyjątkowo wolna. OpenTelemetry rozróżnia head sampling i tail sampling; drugi wariant może wykorzystywać informacje o przebiegu, ale wymaga zasobów i poprawnego zebrania odpowiednich spanów.
Nie należy wyznaczać ogólnego rozkładu ruchu wyłącznie z kolekcji celowo zachowującej wolne ślady. Taki zbiór świetnie nadaje się do diagnozy, ale jest obciążony wyborem. Metryki populacji i szczegółowe przykłady muszą mieć jasno opisane źródło. Brak powolnych śladów może też oznaczać przepełniony kolektor lub utratę kontekstu, a nie brak problemu.
Minimalny kontekst diagnostyczny obejmuje wersje urządzenia i usług, tryb połączenia, wynik operacji, numer próby oraz powód oczekiwania, jeśli jest znany. Nie potrzebuje automatycznie pełnej treści komunikatu ani danych klienta. Identyfikatory śledzenia nie są uprawnieniami, a specyfikacja W3C zabrania umieszczania danych osobowych lub wrażliwych w polach kontekstu służących korelacji.
Procedura: od skargi do potwierdzonej poprawki
Zacznij od jednego zdarzenia widocznego dla użytkownika i zapisz, co oznacza początek oraz koniec. Następnie zbierz identyfikatory i czasy pośrednie, oznaczając lokalne zegary oraz niepewne różnice między maszynami. Rozdziel pracę, oczekiwanie i ponowienia, a w miejscach równoległych ustal ścieżkę krytyczną.
Potem odtwórz problem przy kontrolowanym obciążeniu i wprowadź znane opóźnienie w jednym miejscu. Jeśli instrumentacja je wskazuje, porównaj hipotezy na rzeczywistych wolnych przypadkach. Zmień jeden mechanizm, którego wpływ da się sprawdzić: opróżnianie bufora, obsługę kolejki albo aktualizację widoku. W naszym przykładzie wszystkie trzy mogą dać tę samą sekundę, lecz wymagają różnych działań.
Odbiór poprawki powinien zawierać liczność próby, rozkład od początku do końca, odsetek błędów i utraconych zdarzeń, obciążenie oraz wpływ na zasoby. Zachowaj konfigurację i przypadek, który wcześniej był wolny. Dopiero takie porównanie pozwala powiedzieć, że skrócono oczekiwanie użytkownika, a nie jedynie czas fragmentu, który najłatwiej było zmierzyć.
Źródła
- OpenTelemetry: Traces, spany, propagowanie kontekstu i powiązania operacji asynchronicznych.
- W3C: Trace Context, Recommendation z 23 listopada 2021 r.; format kontekstu HTTP i ograniczenia dotyczące prywatności.
- Google SRE: Monitoring Distributed Systems, obserwacja opóźnień i ograniczenia średnich.
- OpenTelemetry: Sampling, decyzje na początku i po zebraniu śladu oraz koszty próbkowania.
Źródła sprawdzono 21 października 2026 r. Artykuł opisuje zasady, nie deklaruje automatycznej instrumentacji konkretnego SDK. Wszystkie czasy, rozkłady i wyniki optymalizacji pochodzą z deterministycznego modelu syntetycznego.