Я работаю над php-программой, которая разветвляется несколько раз, что приводит к нескольким рабочим процессам, которые затем автономно отрабатывают таблицу задач. Каждый процесс открывает свое собственное соединение mysql (это необходимо из-за разветвленной архитектуры php) и затем должно работать над своей собственной задачей (никакие два рабочих не должны работать над одной и той же задачей). Для этого рабочие «берут» задание, прежде чем приступить к его обработке. Это достигается следующим образом:
where status = 'pending'
чтобы убедиться, что это не было принято в то же время.Сначала я протестировал этот сценарий в оболочке mysql — сделал два соединения с базой данных в двух терминальных окнах. Начал транзакцию в обоих. Затем обновил задачу до «взятой» в первом окне. Пробовал обновить ту же задачу во втором окне. Затем второе окно ожидало, пока я не завершу транзакцию в первом окне, и просто вежливо провалилось (затронуто 0 строк).
Теперь в php (с PDO), как только второй процесс пытается выполнить задачу, я захожу в тупик на базе данных:
Неустранимая ошибка PHP: необработанное исключение «PDOException» с сообщением «SQLSTATE [40001]: сбой сериализации: 1213 Обнаружена тупиковая ситуация при попытке получить блокировку; попробуйте перезапустить транзакцию в …
Я не уверен, если я должен только обернуть фактический обновление задачи до принятой в транзакции. Но, по моему мнению, то, как я делаю это в программе, — это то же самое созвездие, что и в оболочке, и поэтому оно должно работать.
Может ли кто-нибудь помочь мне с тем, что мне здесь не хватает?
Вы в InnoDB, так что это хорошо.
Ваши задачи в таблице? Я думаю, что они есть, но вы не сказали так явно.
Попробуйте эту дисциплину для получения задания, над которым нужно работать.
BEGIN; /* start a transaction */
SELECT task_id, whatever
FROM tasktable
WHERE status = 'pending'
LIMIT 1
FOR UPDATE;
Затем, если вы вернете строку с идентификатором задачи, вы готовы обработать эту задачу. Если вы не получите никаких строк назад, нет никаких отложенных задач.
Отметить задачу как рабочую.
UPDATE tasktable
SET status = 'working'
WHERE task_id = <<the task ID you just got back>>;
Затем,
COMMIT;
Если вы получите исключение взаимоблокировки (вы можете получить его, но оно будет реже), тогда выполните команду ROLLBACK;
, затем попробуйте всю последовательность снова.
Затем выполните задание. Когда это будет сделано, отметьте это как выполненное. Для этого вам не нужна транзакция, потому что вы можете сделать это одним запросом.
UPDATE tasktable
SET status = 'complete'
WHERE task_id = <<the task ID you just got back>>;
Если в вашем клиентском соединении отключен автокоммит, не забудьте зафиксировать после этого запроса.
Я нашел «решение», с которым я иду. Если у кого-то есть другое решение, я готов его услышать и с радостью приму ваш ответ, если в конечном итоге воспользуюсь им.
Таким образом, в основном, пытаясь «взять» задачу в цикле, когда я захожу в тупик, я откатываюсь и пытаюсь снова. Мои первые тесты показывают, что это решает начальную проблему, когда все работники пытаются получить задание.