Yearly Pay yearly and get 2 months free on every paid plan. See plans
Zaloguj się Zacznij za darmo

Strona zużycia pokazywała za dużo, a żadne pieniądze się nie ruszyły

Zacznijmy od tego, co najłatwiej zgubić przy czytaniu reszty: nie pobrano żadnych pieniędzy. Żaden portfel nie został obciążony dwa razy, żadnej karty nie obciążono, żaden limit abonamentu nie został wydany na pracę, któ…

Zacznijmy od tego, co najłatwiej zgubić przy czytaniu reszty: nie pobrano żadnych pieniędzy. Żaden portfel nie został obciążony dwa razy, żadnej karty nie obciążono, żaden limit abonamentu nie został wydany na pracę, która się nie wydarzyła.

Zepsuta była liczba na ekranie. Na części kont podział zużycia na Twojej własnej stronie konta pokazywał za dużo — na najgorszym, jaki zmierzyliśmy, 2,4 raza więcej niż księga, którą ma podsumowywać. Księga była poprawna przez cały czas. Tak samo miesięczne pułapy, które czyta się zupełnie skądinąd. To był defekt wyświetlania, jest naprawiony, a oto jego przyczyna.

Klucz, który szukał czegoś, czego nikt nie zapisał

Praca wynikająca z tego, co zrobiłeś — aktualizacja dziennego podsumowania zużycia, zaksięgowanie polecenia, rozesłanie powiadomienia — nie dzieje się wewnątrz Twojego żądania. Żądanie zapisuje wiadomość, a konsument w tle podnosi ją chwilę później; to właśnie sprawia, że wolny efekt uboczny nie spowalnia szybkiej strony.

Niebezpieczeństwo takiego układu polega na wykonaniu tej samej pracy dwa razy, bo broker chętnie dostarczy tę samą wiadomość ponownie po zakłóceniu. Obroną jest skrzynka odbiorcza: przed przetworzeniem konsument przejmuje wiadomość, zapisując wiersz z kluczem tej wiadomości, a unikalny indeks czyni drugie przejęcie niemożliwym.

Nasz przejmował po parze — najemca, do którego należała wiadomość, plus klucz wiadomości — i wyszukiwał tę parę wobec identyfikatora najemcy niesionego przez kopertę wiadomości, który jest wypełniony.

Wiersze przejęcia nie niosły identyfikatora najemcy w ogóle. Każdy był pusty i był taki od samego początku. Własna dokumentacja encji twierdziła dosłownie, że framework stempluje go przy wstawianiu. Nigdy tego nie robił. Zmierzone na produkcji w dniu odkrycia: wszystkie 3344 wiersze skrzynki odbiorczej puste, podczas gdy 1174 z 1451 wierszy po stronie produkującej niosły prawdziwego najemcę — właśnie dlatego nikt nie podejrzewał strony czytającej.

Wyszukiwanie pytało więc o (najemca, klucz), zapisany wiersz to (nic, klucz), i nigdy nie mogły się zgodzić.

Nic nie zawiodło i dlatego działo się to tygodniami

Wyszukiwanie, które nigdy nie może trafić, brzmi jak coś, co powinno zgłosić błąd przy pierwszej próbie. Zrobiło odwrotnie.

Przejęcie uznało, że wiadomość jest nowa. Wstawienie uderzyło potem w unikalny indeks i zostało odrzucone jako duplikat — słusznie, bo wiersz już tam był. Gałąź awaryjna złapała to odrzucenie i odczytała je życzliwie: pewnie inna dostawa przejęła tę wiadomość, tu nie ma nic do roboty. Koperta została w stanie „jeszcze do przetworzenia", a skoro była jeszcze do przetworzenia, następna dostawa podniosła ją i wykonała pracę znowu.

Każdy konsument przetwarzał od nowa własne zaległości, mniej więcej co czterdzieści pięć sekund, w nieskończoność. Robiło tak dwudziestu sześciu. Jeden produkcyjny wiersz zakończył się 333 godziny po przybyciu — i kończył się raz za razem przez większość tego czasu.

Co naprawdę zepsuło

Z tych dwudziestu sześciu konsumentów czterech kumuluje, zamiast nadpisywać. Ta czwórka to projekcje rozliczeniowe, które czyta strona zużycia, i to tam rzecz wypłynęła.

Mieli jawne zabezpieczenie dokładnie przed tym, napisane po sierpniowym incydencie, w którym pojedyncze zdarzenie zastosowano dziewięć razy do wskaźnika. Zabezpieczenie oznacza wiersz jako udany przed przetworzeniem, żeby ponowna dostawa znalazła oznaczenie i się zatrzymała. Oznaczenie było zapisywane poprawnie za każdym razem. Następne przejęcie szukało go potem tym samym zepsutym kluczem i nie znajdowało. Gwarancja była pusta od dnia wydania.

To, czego ta rzecz nie dotknęła, warto wyliczyć precyzyjnie, bo „projekcja rozliczeniowa" brzmi alarmująco, a fakty nie są alarmujące:

  • Księga była poprawna. Każde wywołanie AI zapisuje dokładnie jeden rekord rozliczeniowy z własną migawką ceny, a nic na tej ścieżce ich nie zapisuje.
  • Żaden portfel się nie ruszył. Salda biorą się z księgi, nigdy z podsumowania.
  • Żaden pułap się nie ruszył. Strażnik budżetu czyta osobny licznik i to on jest jedynym źródłem prawdy o tym, ile wydałeś w tym miesiącu. Nikt nie został odcięty za wcześnie przez zawyżoną liczbę i nikt nie przeszedł, kto nie powinien.

Jedyne, co czyta dotkniętą sumę, to historia zużycia na Twojej własnej stronie konta — Twoja suma i podział na to, na co poszła. To cały promień rażenia, a polegał on na zawyżonym odczycie.

Połowa, na którą nikt nie patrzył

To samo zepsute wyszukiwanie siedziało w drugim miejscu: na ścieżce uruchamianej, gdy wiadomość zawiedzie. Tam nigdy nie znajdowało wiersza, który miało zaktualizować, a tuż pod nim siedziała gałąź z komentarzem „w praktyce nieosiągalne" — zwracająca „ta awaria jest ostateczna" przy każdej awarii.

Licznik ponowień nigdy więc nie ruszał, a żadna zatruta wiadomość nie trafiła do kolejki martwych listów. Cała warstwa obsługi błędów była wyłączona, za komentarzem mówiącym każdemu czytającemu, że nie ma tu czego sprawdzać.

Poprawka

Oba wyszukiwania działają teraz po kluczu wiadomości wewnątrz grupy konsumentów, co i tak wymusza unikalny indeks. Klucze wiadomości wywodzą się z identyfikatora tego, co je wytworzyło, więc z założenia nie mogą kolidować między najemcami.

Dokumentację twierdzącą o stemplowaniu poprawiono, a nie usunięto. To zdanie było pułapką, a jego usunięcie nie zostawiłoby nic, co mówi, że ktoś w końcu sprawdził.

Zachowanie utrwala dziewięć testów. Sześć z nich pada na starym kodzie, co potwierdziliśmy, przywracając stary kod.

Sprzątanie potem

Zawyżone wiersze naprawiają się same. Worker co godzinę odbudowuje dzienne podsumowania z księgi, w oknie dwóch dni. Działał przez cały czas i przegrywał: raz na godzinę przeciw konsumentowi stosującemu od nowa co czterdzieści pięć sekund to nie jest uczciwy wyścig.

Skoro konsument już się nie powtarza, worker wygrywa. Dwa dni wciąż mieszczące się w tym oknie zostały odbudowane z księgi, gdy tylko poprawka trafiła na produkcję. Dwa wcześniejsze dni już z niego wypadły, a to, co na nich zostaje, to nasze własne konto plus jedna pomijalna różnica gdzie indziej — zostawiliśmy je więc, zamiast poszerzać okno na produkcji, żeby to gonić. Wydało się to lepsze niż sugerowanie, że sprzątanie było całkowite.

Co z tego wynieśliśmy

Klucz deduplikacji musi odzwierciedlać to, co indeks faktycznie wymusza, a nie to, co kod uważa za zapisane. Dwie połowy tego klucza karmiono z różnych pól i żadnej nigdy nie zmierzono względem drugiej. Teraz obie są zmierzone, a test, który to udowadnia, świeci na czerwono przy wersji, która była błędna.

Udostępnij LinkedIn X E-mail
Wypróbuj na własnej pracyKażda odpowiedź pokazuje, ile kosztowała. Zacznij od planu bezpłatnego. Zacznij za darmo
Get product updates, not noise New models, features, and changelog highlights - a short email when something actually ships.
Subskrybując, zgadzasz się na otrzymywanie e-maili od ToRun z nowościami produktu. Możesz zrezygnować w dowolnym momencie. Polityka prywatności