Ваша база данных является источником правды здесь, это хранилище всех возможных номеров билетов и билетов, которые были назначены.
Когда вы пытаетесь сделать это внутри своего веб-приложения, всегда существует вероятность коллизий, когда существует несколько одновременно работающих пользователей или хуже, когда ваше приложение распределено по нескольким серверам.
Тем не менее, онобыло бы лучше найти решение базы данных для выдачи этих билетов.
Первое, что вы должны реализовать, - это UNIQUE INDEX
в вашей таблице транзакций, которая записывает номера билетов, тогда БД будет поддерживать целостностьВаши номера всегда:
Вы не предоставили полную схему, поэтому я предполагаю, что в вашей таблице transaction
есть столбцы DrawID
и TicketNumber
CREATE UNIQUE INDEX UX_DrawTicketNumbers
ON transaction (DrawID,TicketNo);
Теперь, когда вы вставите дублирующий номер заявки, БД не выполнит операцию.Основной способ начать - просто обработать сбой и использовать текущую логику, чтобы повторять попытки для следующего номера, пока он не заработает.
Вы можете запросить следующий номер заявки непосредственно из базы данных, используя CTE:
WITH
поддерживается в MySQL начиная с версии 8 (выпущено в 2016 году), если вы используете более старую версию, вы можете заполнить конкретную или временную таблицу всеми возможными номерами билетов, которые можно использовать вместоэто WITH
предложение
SET @DrawID := 1; -- set your drawId here
SELECT TotalTickets into @MaxTicketNo FROM Draw WHERE ID = @DrawID;
WITH RECURSIVE TicketNumbers (n) AS
(
SELECT 1
UNION ALL
SELECT n + 1 FROM TicketNumbers WHERE n < @MaxTicketNo
)
SELECT n FROM TicketNumbers
WHERE n NOT IN (SELECT TicketNo FROM transaction WHERE DrawID = @DrawID)
ORDER BY RAND() LIMIT 1;
По умолчанию MYSQL ограничивает рекурсию до 1000, поэтому вам может потребоваться установить предел рекурсии текущего сеанса на число, большее или равное totalTickets
SET SESSION cte_max_recursion_depth = 1000000;
Теперь вы можете использовать атомарность базы данных, чтобы взять результат вышеупомянутого запроса и использовать его непосредственно в ваших операторах вставки, в SQL Server с использованием CTE я бы просто сделал нечто подобноедля этого для обновления транзакции: (я не могу заставить его работать в БД Fiddle, но концепция должна работать)
SET @paymentId := 1, @token := 'XF-BankResponseNo', @payerID := 234, @drawID := 1;
SELECT TotalTickets into @MaxTicketNo FROM Draw WHERE ID = @DrawID;
WITH RECURSIVE TicketNumbers (n) AS
(
SELECT 1
UNION ALL
SELECT n + 1 FROM TicketNumbers WHERE n < @MaxTicketNo
)
INSERT INTO transaction (PaymentID, TokenID, PayerID, DrawID, TicketNo)
SELECT @paymentId, @token, @PayerID, @DrawID, n
FROM TicketNumbers
ORDER BY Rand() LIMIT 1;
tl; dr;
Чтобы обеспечить атомарность, найдите способ перенести этот процесс в базу данных, создайте хранимую процедуру, чтобы инкапсулировать процесс присвоения номера билета и объединения его с процессом подтверждения платежа.