webhook-proxy
Warstwa pośrednia między naszymi usługami a webhookami Discorda. Kolejkuje zdarzenia per cel, respektuje limity 429 i ponawia błędy 5xx, więc powiadomienia nie giną przy nagłym ruchu. Jeden punkt, przez który przechodzi cała komunikacja wychodząca.
Jeden punkt wyjścia dla całej komunikacji wychodzącej do Discorda. Kolejkuje zdarzenia per cel, respektuje limity 429 z właściwym backoffem i ponawia błędy 5xx, zamiast gubić powiadomienia przy skoku ruchu. Efekt jest prosty do wytłumaczenia: zdarzenie nie przepada tylko dlatego, że akurat trafił się limit.
Wprowadzenie
Nasze serwisy sporo mówią na Discordzie: alarmy z monitoringu, powiadomienia o zdarzeniach, logi operacyjne. Każde z nich idzie webhookiem, a webhooki Discorda mają twarde limity. Dopóki ruch jest spokojny, nikt tego nie czuje. Problem zaczyna się dokładnie wtedy, gdy powiadomienie jest najbardziej potrzebne - przy skoku, gdy kilka serwisów naraz coś wysyła.
webhook-proxy powstał po to, żeby przestać lepić obsługę limitów osobno w każdym serwisie. Zamiast tego cała komunikacja wychodząca idzie przez jeden punkt, który bierze na siebie dostarczenie: kolejkuje, czeka gdy trzeba, ponawia gdy warto i nie gubi zdarzenia po drodze. Dla serwisów po drugiej stronie nic się nie zmienia - wysyłają jak dawniej.
Problem, który widać dopiero pod obciążeniem
W spokojnym ruchu naiwne wysyłanie webhooka działa bez zarzutu i właśnie dlatego jest podstępne. Kod strzela żądaniem, dostaje 2xx, idzie dalej - i nikt nie ma powodu podejrzewać, że coś jest kruche. Krucha jest dopiero ścieżka błędu, która w spokojnym ruchu prawie nigdy się nie odpala.
Wystarczy skok, żeby to wyszło. Kilka serwisów naraz wysyła powiadomienia, wpadasz w limit i dostajesz 429, albo Discord chwilowo oddaje 5xx. W naiwnym podejściu takie zdarzenie po prostu znika: kod poleci dalej, wyjątek najwyżej wyląduje w logu, a na Discordzie brakuje wpisu, który miał tam być. Najgorsze, że gubisz je akurat w momencie największego ruchu, czyli wtedy, gdy powiadomienia są najważniejsze.
Dlatego proxy nie jest optymalizacją wydajności. Jest gwarancją dostarczenia w warunkach, w których naiwna wysyłka cicho zawodzi.
każdy webhook ma własną kolejkę, więc jeden wolny cel nie blokuje pozostałych.
przy limicie proxy czeka dokładnie tyle, ile każe nagłówek Retry-After, a nie na oko.
błąd serwera to backoff wykładniczy i kolejna próba, nie utrata zdarzenia.
błędny payload nie będzie magicznie poprawny za trzecim razem, więc nie marnujemy na niego prób.
Kolejka, która nie blokuje reszty
Pierwsza decyzja to osobna kolejka na każdy cel. Gdyby wszystkie zdarzenia szły jedną wspólną kolejką, jeden wolny albo zablokowany webhook wstrzymałby dostarczanie do wszystkich pozostałych - jeden zatkany cel i cisza wszędzie. Rozdzielenie kolejek sprawia, że problem z jednym webhookiem zostaje przy tym jednym webhooku.
Ta sama struktura daje odporność na skoki. Gdy przychodzi fala, zdarzenia nie są gubione - lądują w kolejce swojego celu i czekają na okno, w którym limit puszcza. Z zewnątrz wygląda to jak chwilowe opóźnienie zamiast utraty, a to zupełnie inna jakość: opóźnione powiadomienie wciąż spełnia swoją rolę, zgubione nie spełnia żadnej.
Serce: pętla dostarczania
Cała logika sprowadza się do jednej decyzji per odpowiedź: sukces kończy, 429 każe poczekać, 5xx ponowić, inne 4xx to koniec bez ponawiania. Rozróżnienie tych przypadków to różnica między proxy, które dostarcza, a takim, które albo gubi zdarzenia, albo w kółko mieli błędny payload.
Asymetria: 429 kontra 5xx
Kluczowa jest asymetria między 429 a 5xx. Przy 429 Discord sam mówi, ile czekać - czytamy to z nagłówka i czekamy dokładnie tyle, ani mniej, ani więcej. Przy 5xx nikt nie mówi nic, więc dokładamy backoff wykładniczy, żeby nie dobijać chwilowo padniętego serwera serią natychmiastowych prób.
Najczęstszy błąd to ponawianie wszystkiego jak leci. Na 429 ponowienie ma sens, ale na 400 czy 404 - nie: payload sam się nie naprawi, a ty tylko szybciej dobijasz do limitu i mielisz bez końca coś, co nigdy nie przejdzie. Ponawiaj to, co przejściowe, i tylko to. Reszta powinna paść od razu, głośno i raz.
Efekt: skok, którego nie widać po drugiej stronie
Dla serwisów po drugiej stronie proxy jest niewidzialne - wysyłają dokładnie tak jak wcześniej i nie wiedzą nic o limitach ani ponawianiu. Cała ta złożoność siedzi w jednym miejscu, zamiast być rozsmarowana po każdym serwisie z osobna. To też znaczy, że poprawka w obsłudze limitów idzie raz, a nie w kilku kodach naraz.
Co proxy robi z odpowiedzią (ilustracyjnie)
Różnica jest widoczna dopiero przy następnej fali ruchu, i właśnie o to chodzi. Powiadomienia nie znikają po cichu w momencie, gdy są najbardziej potrzebne - czekają w kolejce i docierają, gdy limit puszcza. Jeden punkt wyjścia zamienił ciche gubienie zdarzeń w co najwyżej chwilowe opóźnienie, a to jest cała różnica między monitoringiem, któremu można ufać, a takim, który milczy akurat przy pożarze.
Więcej projektów
Inne realizacje z tej samej kategorii - zobacz, jak podchodzimy do podobnych wyzwań.
Masz podobny projekt?
Napisz do nas - wycena jest bezpłatna i wraca w godzinę.



