Czy REST ogranicza się tylko do optymistycznej kontroli współbieżności?

9

Kontekst

Z powodu bezpaństwowości stylu architektonicznego REST polegającego na tym, że każde żądanie jest całkowicie niezależne, co prowadzi serwer do nigdy nie przechowujący żadnych informacji o kliencie.

Dlatego pesymistyczna kontrola współbieżności nie jest odpowiednia, ponieważ wymagałaby od sklepu serwera, który klient otrzymuje blokadę zasobu. Następnie stosuje się optymistyczne sterowanie współbieżnością za pomocą Etagnagłówka. (btw, jak o to prosiłem /programming/30080634/concurrency-in-a-rest-api )

Problem

Główny problem z optymistycznym mechanizmem kontroli współbieżności polega na tym, że cały czas pozwalasz wszystkim klientom wykonywać dowolne operacje.

I chciałbym tego uniknąć, nie naruszając bezpaństwowości REST. Mam na myśli, że wszyscy klienci nie mogą wykonać żadnej operacji w dowolnym momencie.

Pytanie

W moim przekonaniu byłoby to możliwe ze to semi-optymistycznego mechanizm kontroli współbieżności, tak:

  • Klienci mogą poprosić o token
  • Można wygenerować tylko jeden token o ograniczonym okresie ważności
  • Aby wykonać operacje na zasobach (takich jak POST lub PUT ), klient musi podać ten token jako część treści (lub nagłówka?) Żądania. Klient, który nie ma tokena, nie może wykonywać tych operacji.

Jest bardzo podobny do optymistycznej kontroli współbieżności, z tym wyjątkiem, że tylko jeden klient może wykonywać niektóre operacje (ten, który otrzymał token) ... w przeciwieństwie do „wszyscy klienci mogą wykonywać wszystkie operacje”.

Czy ten mechanizm jest zgodny ze stylem architektonicznym REST? Czy łamie którykolwiek ze swoich ograniczeń? Chciałem zapytać o SO, ale wydaje się, że jest to pytanie na wysokim poziomie, związane z projektowaniem oprogramowania.

Ailurus Fulgens
źródło
1
W rzeczywistości jest to raczej proste: musisz Transactionjawnie modelować jako Zasób.
Jörg W Mittag
Co się zmienia? Nie wiem, czy rozumiem twój komentarz. Nie wykonuję kontroli współbieżności transakcji, ale raczej mówiąc klientowi „ok, teraz jesteś absolutnie pewien, że nikt nie zmieni zasobu” (ponieważ otrzymał unikalny token).
AilurusFulgens
1
Jaka jest różnica? Z jednej strony za pomocą Etagu można sprawdzić, kto może aktualizować „bieżącą” wersję; w przypadku kolizji musisz zaktualizować swoją wersję i wykonać aktualizację po tym. Z drugiej strony masz mechanizm „blokujący”: jeden jest dozwolony, inni muszą czekać. Brzmi prawie tak samo. Wadą drugiego podejścia: 2 żądania - 1 dla blokady i 1 dla aktualizacji. W optymalnym przypadku masz tylko jedną aktualizację za pomocą podejścia Etag.
Thomas Junk
Cóż, ogólnie mówiąc o kontroli współbieżności ... sekunda, która „przegrała wyścig” zawsze będzie musiała czekać (tylko z różnych powodów, zgadzam się z tym). Różnica z Etag? Ponieważ Etagnigdy nie masz pewności, że Twoje operacje zostaną zakończone, możesz mieć sytuację, w której nigdy nie będziesz wykonywać żadnych operacji. Z blokadą jesteś pewien, że przynajmniej wykonasz operację. Posiadanie prostej blokady to tylko środek pomiędzy środowiskiem, w którym mogą się zdarzyć „wysokie konflikty” i „rzadkie konflikty”.
AilurusFulgens

Odpowiedzi:

3

Nie należy nigdy (jak nigdy dotąd) blokować żadnego zasobu podczas oczekiwania na interakcję użytkownika.

W pewnym momencie niektórzy z Twoich użytkowników wyruszą na długi weekend, pozostawiając niektóre ważne dane zamknięte.

Ach, ale nie pozwolisz na to, ponieważ masz sprytny plan rozwiązania problemu przekroczenia limitu czasu; to w pewnym momencie okropnie pójdzie nie tak, a użytkownik, który dostał fajny komunikat „Twój widget został zamówiony”, będzie krzyczeć do działu pomocy, żądając dowiedzieć się, dlaczego jego widget nie został dostarczony.

Większość osób może sobie poradzić z komunikatem „przepraszam, że inny użytkownik właśnie zamówił tę część”.

James Anderson
źródło
Dobrze to wytłumaczyłeś. Chociaż twoje pierwsze zdanie nie do końca mnie przekonuje: w pewnym momencie zagwarantujesz, że nikt inny niż ty nie będziesz w stanie wykonać zamówienia. Ale cóż, twoje ostatnie zdanie jest dobrym podsumowaniem, w 99% przypadków tak jest.
AilurusFulgens
1
Jeśli chcesz zablokować rekord dla konkretnego użytkownika, zrób to na poziomie aplikacji. Albo będzie prosty atrybut „LockedBy”, albo bardziej zaawansowany mechanizm kontroli wersji i przepływu pracy.
James Anderson,
Co rozumiesz przez „na poziomie aplikacji”? Jeśli dodasz atrybut „LockedBy” do swojego zasobu, przechowujesz informacje o kliencie na swoim serwerze. A może nie zrozumiałem twojego komentarza. Jeśli możesz podać jakieś szczegóły, byłoby świetnie!
AilurusFulgens
1
@AilurusFulgens - Dodajesz atrybut LockedBy (i ewentualnie znacznik czasu LockedOn) do swojej bazy danych. W kodzie aplikacji ustawiasz nazwę użytkownika i czas za każdym razem, gdy klient rozpoczyna interakcję aktualizacji. Wyłączasz go, gdy klient jest gotowy - musisz wypracować reguły biznesowe, aby rozwiązać konflikty i przekroczenia czasu itp.
James Anderson
2

Używanie tokenów jest bardzo częste w interfejsach API, tokenów tych zwykle wysyłane są jako nagłówki i mają wyraźny cykl życia. Pomyśl na przykład OAuth.

Niezależnie od języka programowania lub frameworku interfejsy API REST są podobne.

Mogę wymyślić kilka scenariuszy, w których chcesz ograniczyć współbieżność, dwa z nich to:

  1. Wielu klientów aktualizuje te same zasoby, jak wiersz bazy danych. Na przykład: dwa współbieżne żądania, jedno usuwa rekord, a drugie próbuje zaktualizować ten sam rekord. W zależności od bazy danych i tego, jak ją skonfigurowałeś, możesz uzyskać blokadę rekordów lub nieprawidłowe operacje, ponieważ dane będą inne lub nie będą istnieć.

  2. Superużytkownik lub administrator wykonujący specjalne czynności z interfejsem API.


Co robić w takich przypadkach?

  1. Użyj transakcji w bazie danych, singletonów, blokad i podobnych mechanizmów, aby zsynchronizować dostęp do zasobów.

  2. Token może działać, myślę, że lepiej będzie, jeśli nie będziesz przechowywać informacji o kliencie, tylko o samym tokenie. W jednym kroku możesz zweryfikować użytkownika i przypisać token. Następnie sprawdź, czy token jest aktywny i czy jest poprawny. Użyj tokena, który można odszyfrować, aby uzyskać dodatkowe informacje. Możesz przechowywać, jeśli jest to specjalny token i zezwalać tylko na raz. W ten sposób weryfikujesz token, a nie użytkownika.

Mam nadzieję, że to pomoże.

Pablo Granados
źródło
Tak, oczekiwałem odpowiedzi na temat podobieństwa tokena i mechanizmu OAuth. Chociaż ostatni jest poświęcony uwierzytelnianiu. Nie zrozumiałem sensu twojego scenariusza 1. Trudną częścią radzenia sobie ze współbieżnością w interfejsie API REST jest utrzymanie ograniczenia bezpaństwowości ... co oznacza, że ​​serwer nie przechowuje informacji o kliencie. Twój scenariusz 2 jest dokładnie tym, co obecnie robię! :)
AilurusFulgens
Czy odpowiedziałem na twoje pytanie?
Pablo Granados
Nie przepraszam Poparłem twoją odpowiedź, ponieważ daje dobry przegląd problemu, ale niestety, tak naprawdę nie rozwiązuje go.
AilurusFulgens
0

REST sam w sobie jest zbyt prymitywny. Możesz zacząć korzystać z REST, ale ostatecznie Twoja bogata aplikacja będzie potrzebowała zapytań z łączeniami i aktualizacji z transakcjami. Każdy programista próbujący dodać te rzeczy na własną rękę byłby podatny na błędy i niespójny. Na szczęście istnieje nowy standard o nazwie OData, który właśnie to robi. Nakłada warstwy na REST i zapewnia (1) język zapytań, który pozwala na proste łączenia za pomocą właściwości nawigacyjnych (bez konieczności ujawniania obcych kluczy), oraz (2) przetwarzanie wsadowe, które obejmuje zestawy zmian atomowych.

Zobacz tutaj (1) https://stackoverflow.com/a/3921423/471129 i,

Zobacz tutaj i (2) https://stackoverflow.com/a/21939972/471129

Erik Eidt
źródło