Czy pojedyncza awaria zakończy się niepowodzeniem operacji zbiorczej?


11

W API, nad którym pracuję, jest operacja usuwania zbiorczego, która akceptuje tablicę identyfikatorów:

["1000", ..., "2000"]

Mogłem zaimplementować operację usuwania według własnego uznania, więc postanowiłem uczynić całą transakcję transakcyjną: to znaczy, jeśli pojedynczy identyfikator jest nieprawidłowy, całe żądanie kończy się niepowodzeniem. Nazywam to trybem ścisłym .

try{
savepoint = conn.setSavepoint();

for(id : IDs)
    if( !deleteItem(id) ){
        conn.rollback(savepoint);
        sendHttp400AndBeDoneWithIt();
        return;
    }

conn.commit();
}

Alternatywą (zaimplementowaną gdzie indziej w naszym pakiecie oprogramowania) jest robienie tego, co możemy w backendu i zgłaszanie awarii w tablicy. Ta część oprogramowania obsługuje mniej żądań, więc odpowiedź nie jest gigantyczną tablicą ... teoretycznie.


Niedawny błąd występujący na serwerze ubogim w zasoby zmusił mnie do ponownego spojrzenia na kod, a teraz kwestionuję moją pierwotną decyzję - tym razem bardziej motywują mnie potrzeby biznesowe niż najlepsze praktyki. Jeśli, na przykład, nie uda mi się całe żądanie, użytkownik będzie musiał spróbować ponownie, a jeśli pewna liczba elementów zostanie usunięta, użytkownik może zakończyć akcję, a następnie poprosić administratora o resztę (podczas pracy nad naprawieniem błędu) !). Byłby to tryb dozwolony .

Próbowałem poszukać wskazówek w tej sprawie, ale wpadłem z pustymi rękami. Przyszedłem więc do ciebie: czego najbardziej oczekują tego rodzaju operacje masowe? Czy powinienem bardziej trzymać się ściśle, czy powinienem być bardziej liberalny?


9
To zależy. Ile kosztuje nieusunięcie czegoś, kiedy powinno być? (Koszt definiowany jako złe dane, ból głowy, niepożądane zachowanie, czas potrzebny na naprawę przez administratora itp.) Czy to jest dopuszczalne? Jeśli potrafisz żyć z konsekwencjami nie zawierania wszystkiego, idź. Jeśli spowodowałoby to zbyt duży problem, nie rób tego. Znasz swoje oprogramowanie i jego konsekwencje, więc musisz podjąć decyzję.
Becuzz,

1
@Becuzz Kosztem byłby użytkownik zauważający jedną lub dwie resztki i otwierający bilet na ten temat; obecna sytuacja to „omg delete is broken”. Na szczęście użytkownik jest na korytarzu, więc tym razem nie stanowi to większego problemu. Chodzi o to, że lubię robić właściwe rzeczy, gdy tylko jest to możliwe, a mając ponad 10-letnią bazę kodową, Bóg wie, że niektóre rzeczy mogą stać się prawidłowe
rath,

Myślę, że zależy to również od tego, czy chcesz skalowalność, czy nie. Jeśli nie masz wielu identyfikatorów, nie powinno to mieć większego znaczenia. Jeśli zamierzasz mieć milion identyfikatorów, lub jeszcze lepiej, nie masz absolutnej pewności, że tak się nie stanie, możesz poświęcić godzinę na usunięcie identyfikatorów, aby całkowicie zresetować z powodu 1 nieprawidłowego identyfikatora.
imnota4

1
@ imnota4 Doskonały punkt, którego nie wziąłem pod uwagę. Interfejs użytkownika ogranicza żądanie do maksymalnie około 250, ale backend nie ma ograniczeń. Czy mogę prosić o ponowne opublikowanie komentarza?
rath,

1
Tryb zezwalający ułatwia także pracę administratorów, ponieważ nie muszą oni odtwarzać błędów z całym stosem identyfikatorów. Przydatne może być również podanie w odpowiedzi przyczyny każdego błędu. Patrząc na przyczynę, użytkownik końcowy może rozwiązać problem bez biletów „omg delete is broken”.
Laiv

Odpowiedzi:


9

Można wykonać „ścisłą” lub „ładną” wersję usuwanego punktu końcowego, ale musisz wyraźnie powiedzieć użytkownikowi, co się stało.

Wykonujemy akcję usuwania z tym punktem końcowym. Prawdopodobnie DELETE /resource/bulk/lub coś podobnego. Nie jestem wybredna. Liczy się tutaj to, że bez względu na to, czy zdecydujesz się być surowy, czy miły, musisz zgłosić dokładnie to, co się stało.

Na przykład interfejs API, z którym współpracowałem, miał DELETE /v1/student/punkt końcowy, który akceptował identyfikatory zbiorcze. Regularnie wysyłaliśmy żądanie podczas testowania, otrzymywaliśmy 200odpowiedź i zakładaliśmy, że wszystko jest w porządku, ale później dowiedzieliśmy się, że wszyscy na liście byli jeszcze w bazie danych (ustawiona jako nieaktywna) lub nie zostali faktycznie usunięci z powodu błędu, który zawiedliśmy przyszłe połączenia, GET /v1/studentponieważ otrzymaliśmy dane, których się nie spodziewaliśmy.

Rozwiązanie pojawiło się w późniejszej aktualizacji, która dodała treść do odpowiedzi z identyfikatorami, które nie zostały usunięte. Według mojej wiedzy jest to rodzaj najlepszej praktyki.

Podsumowując, bez względu na to, co robisz, upewnij się, że zapewnisz sposób, aby użytkownik końcowy wiedział, co się dzieje i być może dlaczego. IE, jeśli wybraliśmy ścisły format, odpowiedź może być 400 - DELETE failed on ID 1221 not found. Gdybyśmy wybrali „ładną” wersję, mogłaby to być 207 - {message:"failed, some ids not deleted", failedids:{1221, 23432, 1224}}(przepraszam za moje złe formatowanie JSona).

Powodzenia!


6
207 Multi-Statusmoże być odpowiednie dla tej częściowej reakcji na awarię
Richard Tingle

1
NO TO JEDZIEMY! Właściwie to nie pamiętam! Zamierzam zaktualizować odpowiedź, ponieważ tak naprawdę jest to zgodne ze standardem.
Adam Wells,

2

Należy być surowym i tolerancyjnym.

Zwykle ładunki masowe są podzielone na 2 fazy:

  • Uprawomocnienie
  • Ładowanie

Na etapie sprawdzania poprawności każdy rekord jest sprawdzany ściśle, aby upewnić się, że spełnia wymagania specyfikacji danych. Można łatwo sprawdzić 10 z 1000 rekordów w ciągu zaledwie kilku sekund. Prawidłowe rekordy są umieszczane w nowym pliku do załadowania, nieprawidłowy (e) oflagowane i usunięte i zwykle umieszczane w osobnym pliku (pomiń plik). Powiadomienie jest następnie wysyłane do rekordów, które nie przeszły sprawdzania poprawności, dzięki czemu można je sprawdzić i zdiagnozować w celu rozwiązania problemów.

Po sprawdzeniu poprawności dane są następnie ładowane. Zwykle jest ładowany partiami, jeśli jest wystarczająco duży, aby uniknąć długotrwałych transakcji lub jeśli wystąpi awaria, łatwiej będzie go odzyskać. Rozmiar partii zależy od tego, jak duży jest zestaw danych. Jeśli ma się tylko kilka 1000 rekordów, jedna partia byłaby OK. Tutaj możesz być nieco tolerancyjny w przypadku awarii, ale możesz chcieć ustawić nieudany próg wsadowy, aby zatrzymać całą operację. Być może, jeśli partie [N] zawiodą, można by zatrzymać całą operację (gdyby serwer był wyłączony lub coś podobnego). Zwykle w tym momencie nie ma awarii, ponieważ dane zostały już sprawdzone, ale jeśli wystąpiły z powodu problemów środowiskowych lub innych, po prostu załaduj ponownie partię, która się nie powiodła. To sprawia, że ​​odzyskiwanie jest trochę łatwiejsze.


Nie sprawdzam poprawności identyfikatorów w stosunku do wartości DB, po prostu próbuję je usunąć i zobaczyć, jak to pójdzie, bo to potrwa wieczność. Przerwanie po awarii N wydaje się bardzo rozsądną sugestią, +1
rath

2

Czy pojedyncza awaria zakończy się niepowodzeniem operacji zbiorczej?

Nie ma na to kanonicznej odpowiedzi. Konieczne jest zbadanie potrzeb i konsekwencji dla użytkownika oraz ocena kompromisów. OP podał niektóre z wymaganych informacji, ale oto, jak mam postępować:

Pytanie 1 : „Jakie są konsekwencje dla użytkownika, jeśli indywidualne usunięcie się nie powiedzie?”

Odpowiedź powinna kierować resztą zachowań projektowych / wdrożonych.

Jeśli, jak stwierdzono w OP, to po prostu użytkownik zauważy wyjątek i otworzy zgłoszenie problemu, ale w inny sposób nie zostanie zmieniony (nie usunięte elementy nie wpływają na kolejne zadania), to wybrałbym zezwolenie z automatycznym powiadomieniem Tobie.

Jeśli nieudane usunięcia muszą zostać usunięte, zanim użytkownik będzie mógł kontynuować, ścisłe jest zdecydowanie preferowane.

Danie użytkownikowi opcji (np. Zasadniczo ignorowanie awarii jako domyślnych albo ścisłych lub dopuszczających) może być najbardziej przyjaznym dla użytkownika podejściem.

Pytanie 2 : „Czy wystąpiłyby jakiekolwiek problemy ze spójnością / spójnością danych, gdyby kolejne zadania były wykonywane z nieod usuniętymi elementami w magazynie danych?”

Ponownie, odpowiedź zapewniłaby najlepszy projekt / zachowanie. Tak -> Surowe, Nie -> Dopuszczalne, Może -> Surowe lub Wybrany przez użytkownika (szczególnie jeśli użytkownik może polegać na dokładnym określeniu konsekwencji).


0

Myślę, że zależy to od tego, czy chcesz skalowalność, czy nie. Jeśli nie masz wielu identyfikatorów, nie powinno to mieć większego znaczenia. Jeśli zamierzasz mieć milion identyfikatorów, a jeszcze lepiej, nie masz absolutnej pewności, że tak się nie stanie, możesz poświęcić godzinę na usunięcie identyfikatorów, aby całkowicie zresetować z powodu 1 nieprawidłowego identyfikatora.


-1

Powiedziałbym, że jednym ważnym punktem jest to, co oznacza usunięcie większości rzeczy.

Czy te identyfikatory są w jakiś sposób powiązane logicznie, czy to tylko wygoda / wydajność - grupowanie ich w partiach?

W przypadku jakiegoś, nawet luźnego połączenia, wybrałbym strict. Jeśli jest to tylko tryb wsadowy (np. Użytkownik klika „zapisz” dla ostatnich minut pracy i dopiero wtedy partia jest przesyłana), wybrałbym permissivewersję.

Zgodnie z drugą odpowiedzią: W każdym razie powiedz „użytkownikowi” dokładnie, co się stało.

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.