Jak dotąd, jest to dobre, przynajmniej uniemożliwi to użytkownikowi dokonywanie płatności w wielu sesjach (wielokrotne próby zakupu tej samej karty - dobrze radzić sobie z podwójnymi kliknięciami).
Jak sprawdzasz? Za pomocą standardowego SELECT
lub z SELECT ... FOR UPDATE
? Na podstawie kroku 5 domyślam się, że sprawdzasz zarezerwowaną kolumnę w elemencie lub coś podobnego.
Problem polega na tym, że SELECT ... FOR UPDATE
w kroku 2 NIE zastosuje FOR UPDATE
zablokować wszystko inne. Ma zastosowanie tylko do tego, co jest SELECT
ed:cart-item
stół. Na podstawie nazwy będzie to inny rekord dla każdego koszyka/użytkownika. Oznacza to, że inne transakcje NIE zostaną zablokowane.
Zgodnie z powyższym, w oparciu o podane przez Ciebie informacje, możesz skończyć z wieloma osobami kupującymi ten sam przedmiot, jeśli nie używasz SELECT ... FOR UPDATE
w kroku 3.
Sugerowane rozwiązanie
- Rozpocznij transakcję
SELECT ... FOR UPDATE
cart-item
stół.
Spowoduje to zablokowanie podwójnego kliknięcia przed uruchomieniem. To, co tutaj wybierzesz, powinno być rodzajem kolumny „zamówione w koszyku”. Jeśli to zrobisz, druga transakcja zatrzyma się tutaj i poczeka na zakończenie pierwszej, a następnie odczyta wynik, który pierwsza zapisała w bazie danych.
Pamiętaj, aby zakończyć proces płatności tutaj, jeśli cart-item
tabela mówi, że została już zamówiona.
SELECT ... FOR UPDATE
tabela, w której zapisujesz, czy przedmiot został zarezerwowany.
Uniemożliwi to INNYM koszykom/użytkownikom odczytanie tych elementów.
Na podstawie wyniku, jeśli pozycje nie są zarezerwowane, kontynuuj:
-
UPDATE ...
tabeli w kroku 3, oznaczając pozycję jako zastrzeżoną. Wykonaj dowolne inneINSERT
s iUPDATE
s ty też potrzebujesz. -
Dokonać płatności. Wycofaj, jeśli usługa płatności twierdzi, że płatność nie zadziałała.
-
Zarejestruj płatność, jeśli się powiedzie.
-
Zatwierdź transakcję
Upewnij się, że nie robisz niczego, co mogłoby się nie powieść między krokami 5 i 7 (np. wysyłanie e-maili), w przeciwnym razie możesz skończyć z dokonaniem płatności bez jej zarejestrowania, w przypadku cofnięcia transakcji.
Krok 3 jest ważnym krokiem, jeśli chodzi o upewnienie się, że dwie (lub więcej) osoby nie próbują zamówić tego samego przedmiotu. Jeśli dwie osoby spróbują, druga osoba zawiesi się na swojej stronie podczas przetwarzania pierwszej. Następnie, gdy skończy się pierwszy, drugi przeczyta kolumnę „zarezerwowane” i możesz zwrócić użytkownikowi wiadomość, że ktoś już kupił ten przedmiot.
Płatność w transakcji lub nie
To jest subiektywne. Ogólnie rzecz biorąc, chcesz zamykać transakcje tak szybko, jak to możliwe, aby uniknąć zablokowania wielu osób w jednoczesnej interakcji z bazą danych.
Jednak w tym przypadku naprawdę chcesz, aby poczekali. To tylko kwestia czasu.
Jeśli zdecydujesz się zatwierdzić transakcję przed dokonaniem płatności, będziesz musiał zapisać swój postęp w jakiejś tabeli pośredniej, uruchomić płatność, a następnie zapisać wynik. Pamiętaj, że jeśli płatność się nie powiedzie, będziesz musiał ręcznie cofnąć zaktualizowane rekordy rezerwacji pozycji.
WYBIERZ... DO AKTUALIZACJI w nieistniejących wierszach
Tylko słowo ostrzeżenia, na wypadek gdyby projekt tabeli wymagał wstawiania wierszy, w których trzeba wcześniej SELECT ... FOR UPDATE
:Jeśli wiersz nie istnieje, ta transakcja NIE spowoduje, że inne transakcje będą czekać, jeśli również SELECT ... FOR UPDATE
ten sam nieistniejący wiersz.
Dlatego upewnij się, że zawsze serializujesz swoje żądania, wykonując SELECT ... FOR UPDATE
w rzędzie, o którym wiesz, że istnieje jako pierwszy. Następnie możesz SELECT ... FOR UPDATE
w wierszu, który może jeszcze nie istnieć. (Nie próbuj robić tylko SELECT
w wierszu, który może istnieć lub nie, ponieważ będziesz odczytywał stan wiersza w momencie rozpoczęcia transakcji, a nie w momencie uruchomienia SELECT
. A więc SELECT ... FOR UPDATE
w nieistniejących wierszach jest nadal czymś, co musisz zrobić, aby uzyskać najbardziej aktualne informacje, pamiętaj tylko, że nie spowoduje to, że inne transakcje będą czekać).