Timeout nie oznacza, że operacja się nie wykonała
Klient wysyła żądanie do serwera i czeka na odpowiedź. Jeśli mija ustalony czas, otrzymuje timeout. Intuicyjnie brzmi to jak „operacja się nie udała”, ale system rozproszony nie daje takiej pewności. Serwer mógł w ogóle nie dostać żądania, mógł je właśnie wykonywać albo mógł już zakończyć pracę, a zagubiła się tylko odpowiedź. Dlatego po timeoutcie klient często wie jedynie, że nie zna wyniku. Ta niepewność jest powodem, dla którego bezpieczne ponawianie żądań wymaga idempotencji, identyfikatorów operacji albo późniejszego sprawdzania stanu.
Timeout mierzy czekanie klienta, nie historię serwera
Timeout jest lokalną decyzją programu: „nie będę czekał dłużej niż X”. Nie jest komunikatem z drugiej maszyny. Zanim upłynie limit, żądanie przechodzi przez stos sieciowy, routery, load balancery, kolejki i kod serwera, a odpowiedź wraca podobną drogą. Brak odpowiedzi w terminie nie ujawnia, na którym etapie pojawiło się opóźnienie lub awaria.
Możliwy jest najgorszy wariant: sukces bez potwierdzenia
Wyobraźmy sobie polecenie utworzenia zasobu. Serwer odbiera je, zapisuje zmianę i odsyła odpowiedź „OK”. W drodze do klienta połączenie zostaje zerwane. Klient widzi timeout, choć operacja została wykonana. Jeżeli naiwnie wyśle identyczne polecenie ponownie, może stworzyć drugi zasób. Amazon opisuje właśnie ten problem, wyjaśniając, dlaczego retry musi być projektowany razem z semantyką operacji.
Dlatego błąd sieciowy jest często wynikiem „nieznanym”
W aplikacji lokalnej wyjątek po operacji może oznaczać, że określony fragment kodu nie został wykonany. Przy zdalnym wywołaniu granica jest mniej wyraźna. Serwer i klient mają oddzielne pamięci oraz własne punkty awarii. Klient może wiedzieć, że nie dostał wyniku, ale nie ma automatycznie dowodu, że serwer nie zmienił świata.
Idempotencja zamienia niepewność w coś łatwiejszego do obsługi
Jeżeli API pozwala dołączyć unikalny identyfikator intencji, serwer może rozpoznać powtórzenie tej samej operacji. AWS opisuje idempotentne interfejsy właśnie jako sposób na bezpieczne retry. Zamiast zgadywać, czy pierwsza próba się udała, klient może powtórzyć żądanie z tym samym identyfikatorem, a serwer zwrócić wynik pierwszego wykonania albo uniknąć ponownego skutku ubocznego.
To problem wiedzy, a nie tylko niezawodności
Najciekawsze jest to, że timeout niekoniecznie oznacza awarię którejkolwiek strony. Oba programy mogły wykonać wszystko prawidłowo, a jedynym problemem było opóźnienie lub zerwane połączenie. Mimo to klient traci pewność co do rezultatu. Systemy rozproszone są pełne takich sytuacji, w których głównym problemem nie jest „co się stało?”, lecz „co uczestnicy mogą wiarygodnie wiedzieć o tym, co się stało?”.
Retry bez tożsamości operacji zmienia pytanie
Gdy klient po timeoutcie tworzy nowe żądanie bez identyfikatora poprzedniej intencji, serwer nie ma sposobu, by wiedzieć, czy chodzi o powtórzenie czy o drugą legalną operację. Dlatego samo porównanie parametrów bywa niewystarczające: użytkownik może naprawdę chcieć dwa razy kupić ten sam produkt albo uruchomić dwie identyczne maszyny. Unikalny token pozwala powiedzieć: „to jest ta sama próba logiczna, nawet jeśli transport sieciowy wygląda jak nowe wywołanie”.
Timeout trzeba dobierać do pełnej ścieżki
Zbyt długi timeout zatrzymuje zasoby klienta i opóźnia wykrycie prawdziwej awarii. Zbyt krótki może sam wytwarzać błędy, bo normalne, lecz wolniejsze odpowiedzi będą porzucane i ponawiane. AWS ostrzega, że zbyt agresywne timeouts mogą zwiększać ruch i latencję przez nadmierne retries. Ustawienie limitu czasu nie jest więc jedynie parametrem UX; wpływa na obciążenie całego łańcucha zależności.
Dobrym testem projektu jest pytanie, co zobaczy operator po incydencie. Jeżeli log klienta mówi tylko „timeout”, a log serwera „success”, trzeba mieć wspólny identyfikator żądania, żeby połączyć te dwa fakty. Idempotency key pomaga więc nie tylko w samym wykonaniu, ale też w observability i późniejszym dochodzeniu, czy dana intencja została zrealizowana. Bez takiej korelacji zespół może próbować naprawiać problem, który w rzeczywistości jest wyłącznie problemem potwierdzenia wyniku.