Jak napisać testy względem ostatecznie spójnej usługi?


17

Buduję usługę w oparciu o Google App Engine Datastore, który jest ostatecznie spójnym magazynem danych. Dla mojej aplikacji jest w porządku.

Jednak rozwijam testy, które robią takie rzeczy jak obiekt PUT, a następnie GET obiekt i sprawdzanie właściwości na zwróconym obiekcie. Niestety, ponieważ magazyn danych jest ostatecznie spójny, te proste testy nie są powtarzalne.

Jak testujesz ostatecznie spójną usługę?


2
Dlaczego przede wszystkim testujesz, oczekując odtwarzalności w stosunku do usługi zewnętrznej?

... a co tak naprawdę próbujesz przetestować? Twój kod? czy Google?

5
Testuję cały system. To są testy integracyjne, a nie testy jednostkowe.
Doug Richardson,

3
How can I reproducibly test an eventually consistent service? - Nie możesz. Musisz usunąć słowo „odtwarzalnie” lub słowo „w końcu”; nie możesz mieć obu.
Robert Harvey,

1
Jeśli w końcu będzie spójny, bez względu na to, czy jest odtwarzalny, czy nie, wynik będzie udany. Powiedziałeś już, że dla twojej aplikacji jest w porządku, więc co naprawdę testujesz? Ewentualność? Integracja z GAE? Twój kod?
Laiv

Odpowiedzi:


16

Podczas projektowania testów funkcjonalnych weź pod uwagę niefunkcjonalne wymagania - jeśli twoja usługa ma niefunkcjonalne wymaganie „Spójne w ciągu x (sekund / minut / etc)”, po prostu uruchom żądania PUT, poczekaj x, a następnie uruchom żądania GET.

W tym momencie, jeśli dane jeszcze nie „dotarły”, możesz uznać żądanie PUT za niezgodne z Twoimi wymaganiami.


7

Naprawdę chcesz, aby twoje testy były szybkie i spójne. Jeśli zaczniesz tworzyć testy, które czasami mogą się nie powieść ze względu na ostateczną spójność, zignorujesz test, gdy się nie powiedzie, a następnie jaki jest jego użytek?

Utwórz fałszywą usługę, która obsługuje żądania PUT i GET, ale ma dodatkową operację zapewniającą spójność. Twój test to:

datastore.do_put(myobj);
datastore.make_consistent();
validate(datastore.do_get(), myobj);

Pozwala to przetestować zachowanie oprogramowania, gdy GET pomyślnie pobiera obiekt PUT. Pozwala także przetestować zachowanie oprogramowania, gdy GET nie znajduje obiektu (lub poprawnego obiektu) z powodu niespójności usługi. Po prostu pomiń połączenie z make_consistent().

Wciąż warto mieć testy, które wchodzą w interakcję z prawdziwą usługą, ale powinny one działać poza normalnym procesem programowania, ponieważ nigdy nie będą w 100% niezawodne (np. Jeśli usługa nie działa). Testy te powinny być wykorzystane do:

  1. dostarczać mierników średnio i najgorszego czasu między PUT a kolejnym GET, który staje się spójny; i
  2. sprawdź, czy Twoja fałszywa usługa zachowuje się podobnie do prawdziwej usługi. Zobacz https://codewithoutrules.com/2016/07/31/verified-fakes/

6

Ok, więc. „Co testujesz” to kluczowe pytanie.

  • Testuję moją wewnętrzną logikę tego, co się dzieje, zakładając, że Google działa

W takim przypadku powinieneś wyśmiewać usługi Google i zawsze zwracać odpowiedź.

  • Testuję moją logikę, aby poradzić sobie z przejściowymi błędami, o których wiem, że Google go wygeneruje

W takim przypadku powinieneś wyśmiewać usługi Google i zawsze zwracać przejściowy błąd przed prawidłową odpowiedzią

  • Testuję, że mój produkt rzeczywiście będzie działał z prawdziwą usługą Google

Powinieneś wstrzyknąć prawdziwe usługi Google i uruchomić test. Ale! Testowany kod powinien mieć wbudowaną obsługę błędów przejściowych (ponownych prób). Więc powinieneś otrzymać spójną odpowiedź. (chyba że Google zachowuje się bardzo źle)


+1 za sugestię Mock - dałbym więcej głosów za dodatkowe opcje, gdybym mógł.
mcottle,

6

Użyj jednego z poniższych:

  • Po PUT spróbuj ponownie GET N razy, aż do sukcesu. Niepowodzenie, jeśli nie uda się po N próbach.
  • Sen pomiędzy PUT a GET

Niestety musisz wybrać magiczne wartości (N lub czas snu) dla obu tych technik.


1
Czy możesz wyjaśnić: czy są to alternatywy lub uzupełnienia? Myślę, że chcesz powiedzieć, że są alternatywami - i tak o nich myślę. Ale może się mylę.
Robin Green,

1
Zgadza się, chciałem, żeby były alternatywami.
Doug Richardson,

2

Jak rozumiem, magazyn danych Google Cloud umożliwia zarówno bardzo spójne, jak i ostatecznie spójne zapytania .

Kompromis polega na tym, że bardzo spójne zapytania są dość mocno ograniczone stawką (coś, z czym można żyć podczas testowania).

Jedną z możliwości może być umieszczenie zapytań w magazynie danych w opakowaniu, które może zapewnić silną spójność do celów testowych.

Na przykład możesz mieć wywołane metody start_debug_strong_consistency()i end_debug_strong_consistency().

Metoda początkowa utworzyłaby klucz, który może być użyty jako klucz nadrzędny dla wszystkich kolejnych zapytań, a metoda końcowa usunęłaby klucz.

Jedyną zmianą rzeczywistych zapytań, które testujesz, byłoby wywołanie, setAncestor(your_debug_key)jeśli ten klucz istnieje.


1

Jednym z podejść, które jest dobre w teorii, ale nie zawsze praktyczne, jest uczynienie wszystkich operacji zapisu w testowanym systemie idempotentnymi . Oznacza to, że zakładając, że kod testowy testuje rzeczy w ustalonej kolejności sekwencyjnej, możesz ponowić wszystkie odczyty i wszystkie zapisy pojedynczo, aż uzyskasz oczekiwany wynik, ponawiając próbę, aż przekroczony zostanie limit czasu zdefiniowany w kodzie testowym. To znaczy, zrób rzecz A1, ponów próbę, jeśli to konieczne, aż wynik będzie B1, a następnie zrób rzecz A2, ponów próbę, jeśli to konieczne, aż wynik będzie B2 i tak dalej.

Następnie nie musisz się martwić, aby sprawdzić warunki wstępne operacji zapisu, ponieważ operacje zapisu będą już je sprawdzać, a ty po prostu ponów próbę, aż się powiedzie!

Używaj tych samych „domyślnych” limitów czasu, jak to tylko możliwe, które można zwiększyć, jeśli cały system działa wolniej, i zastępuj wartości domyślne indywidualnie podczas ponawiania szczególnie wolnych operacji.


1

Usługa taka jak Google App Engine Datastore opiera się na replikacji danych w kilku globalnie rozmieszczonych punktach obecności (POP). Każdy test integracyjny dla ostatecznie spójnej usługi jest tak naprawdę sprawdzianem szybkości replikacji tej usługi w całym zestawie POP. Szybkość rozprzestrzeniania się treści na każdy POP w danej usłudze nie będzie taka sama dla każdego POP w ramach usługi, w zależności od wielu czynników, takich jak metoda replikacji i różne problemy z transportem internetowym - to dwa przykłady które stanowią większość raportów w dowolnej ostatecznie spójnej usłudze magazynu danych (przynajmniej takie było moje doświadczenie podczas pracy dla dużej sieci CDN).

Aby skutecznie przetestować replikację obiektu na danej platformie, należy ustawić test tak, aby żądał tego samego ostatnio umieszczonego obiektu od konkretnie każdego z punktów POP usługi. Sugeruję przetestowanie listy POP jeden do pięciu razy lub do czasu, aż wszystkie POP na liście POP zgłoszą posiadanie obiektu. Oto zestaw interwałów, w których można wykonać test, który możesz dostosować: 1, 5, 60 minut, 12 godzin, 25 godzin po umieszczeniu go w magazynie danych. Kluczem jest rejestrowanie wyników w każdym interwale w celu późniejszego przeglądu i analizy w celu sprawdzenia zdolności danej usługi do globalnej replikacji obiektów. Często usługi magazynu danych pobierają lokalną kopię do POP tylko wtedy, gdy zażądano jej lokalnie [routing odbywa się za pomocą protokołu BGP, dlatego test musi żądać obiektu od każdego konkretnego POP, aby był globalnie ważny dla danej platformy] . W przypadku magazynu danych Google zastanawiasz się nad przygotowaniem testu do zapytania danego obiektu z „ponad 70 punktów obecności w 33 krajach”; prawdopodobnie będziesz musiał uzyskać listę adresów URL określonych adresów POP od pomocy technicznej Google [zob .:https://cloud.google.com/about/locations/ ] lub jeśli Google używa Fastly do replikacji, Fastly Support [ https://www.fastly.com/resources ].

Kilka zalet tej metody: 1) Poznasz platformę replikacji danej usługi, poznasz jej zalety i słabe punkty jako całość w skali globalnej [tak jak było podczas testu integracji]. 2) Dla każdego testowanego obiektu będziesz mieć narzędzie do podgrzewania treści [zrób pierwsze żądanie, które tworzy kopię na danym lokalnym POP] - zapewniając w ten sposób sposób na globalne rozpowszechnianie treści, zanim klienci zażądają tego od gdziekolwiek na ziemi.


0

Mam doświadczenie z Google App Engine Datastore. Dziwne, działając lokalnie, często jest bardziej „w końcu” niż „konsekwentne”. Najprostszy przykład: utwórz nowy byt, a następnie pobierz go. Często w ciągu ostatnich 5 lat widziałem lokalnie działający zestaw SDK nie znajdujący nowego obiektu natychmiast, ale znajdujący go po około pół sekundy.

Jednak w porównaniu z prawdziwymi serwerami Google nie widziałem takiego zachowania. Starają się, aby klient Datastore zawsze działał na tym samym serwerze po swojej stronie, więc zwykle wszelkie zmiany są natychmiast odzwierciedlane w zapytaniach.

Moja rada dotycząca testów integracyjnych polega na uruchomieniu ich na prawdziwych serwerach, a wtedy prawdopodobnie nie będziesz musiał wprowadzać fałszywych zapytań ani opóźnień, aby uzyskać wyniki.


Chociaż jest to wygodne, może powodować subtelne awarie obejmujące wiele serwerów aplikacji, które nie zostaną wykryte w testach integracyjnych. Wydaje mi się, że ostatecznie poprawili lokalny serwer z dobrego powodu!
Robin Green,
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.