Дотук добре, това най-малкото ще попречи на потребителя да извършва плащане в множество сесии (многократни опити за плащане на една и съща карта – добре да се справите с двойните щраквания.)
Как проверявате? Със стандартен SELECT
или с SELECT ... FOR UPDATE
? Въз основа на стъпка 5 предполагам, че проверявате запазена колона за елемента или нещо подобно.
Проблемът тук е, че SELECT ... FOR UPDATE
в стъпка 2 НЯМА да приложи FOR UPDATE
заключете за всичко останало. Прилага се само за това, което е SELECT
ed:cart-item
маса. Въз основа на името, това ще бъде различен запис за всяка количка/потребител. Това означава, че други транзакции НЯМА да бъдат блокирани от продължаване.
Следвайки горното, въз основа на информацията, която сте предоставили, може да се окажете, че няколко души купуват един и същ артикул, ако не използвате SELECT ... FOR UPDATE
на стъпка 3.
Предложено решение
- Започнете транзакцията
SELECT ... FOR UPDATE
cart-item
маса.
Това ще блокира стартирането с двойно щракване. Това, което изберете тук, трябва да бъде някаква колона "поръчана количка". Ако направите това, втора транзакция ще спре тук и ще изчака първата да приключи и след това ще прочетете резултата, който първата е записала в базата данни.
Уверете се, че сте приключили процеса на плащане тук, ако cart-item
таблицата казва, че вече е поръчана.
SELECT ... FOR UPDATE
таблицата, в която записвате, ако даден артикул е запазен.
Това ще заключи ДРУГИ колички/потребители от възможността да четат тези елементи.
Въз основа на резултата, ако артикулите не са запазени, продължете:
-
UPDATE ...
таблицата в стъпка 3, като маркирате елемента като запазен. Направете всяко другоINSERT
s иUPDATE
и вие имате нужда. -
Извършвам плащане. Извършете връщане назад, ако платежната услуга каже, че плащането не работи.
-
Запишете плащането, ако е успешно.
-
Извършете транзакция
Уверете се, че не правите нищо, което може да се провали между стъпки 5 и 7 (като изпращане на имейли), в противен случай може да се окажете те да направят плащане без то да бъде записано, в случай че транзакцията бъде отменена.
Стъпка 3 е важната стъпка по отношение на това да се уверите, че двама (или повече) души не се опитват да поръчат един и същ артикул. Ако двама души се опитат, вторият човек в крайна сметка ще "увисне", докато обработва първия. След това, когато първият приключи, вторият ще прочете колоната „резервирано“ и можете да върнете съобщение на потребителя, че някой вече е закупил този артикул.
Плащане в транзакция или не
Това е субективно. Обикновено искате да затваряте транзакциите възможно най-бързо, за да избегнете блокиране на взаимодействието на няколко души с базата данни наведнъж.
В този случай обаче наистина искате да изчакат. Въпросът е само колко време.
Ако решите да извършите транзакцията преди плащане, ще трябва да запишете напредъка си в някаква междинна таблица, да изпълните плащането и след това да запишете резултата. Имайте предвид, че ако плащането не успее, ще трябва ръчно да отмените записите за резервация на артикули, които сте актуализирали.
ИЗБЕРЕТЕ... ЗА АКТУАЛИЗИРАНЕ на несъществуващи редове
Само едно предупреждение, в случай че дизайнът на таблицата ви включва вмъкване на редове, където трябва по-рано SELECT ... FOR UPDATE
:Ако ред не съществува, тази транзакция НЯМА да накара други транзакции да чакат, ако те също SELECT ... FOR UPDATE
същия несъществуващ ред.
Така че, не забравяйте винаги да сериализирате заявките си, като правите SELECT ... FOR UPDATE
на ред, за който знаете, че съществува първи. След това можете да SELECT ... FOR UPDATE
на реда, който може да съществува или все още не съществува. (Не се опитвайте да правите само SELECT
на реда, който може или не може да съществува, тъй като ще четете състоянието на реда в момента на стартиране на транзакцията, а не в момента, в който стартирате SELECT
. И така, SELECT ... FOR UPDATE
на несъществуващи редове все още е нещо, което трябва да направите, за да получите най-актуалната информация, само имайте предвид, че това няма да накара други транзакции да чакат.)