Как реализовать, что бы несколько воркеров не выполняли одну и ту же задачу.
Допустим нужно отправить письмо с сайта(ну или другое действие, не важно).
Клиент нажимает на кнопку, я это письмо записываю в бд - т.е. создаю задачу.
Дальше у меня крутиться демон(воркер, скрипт который обрабатывает очередь)
while (true) {
//Код, который лезет в бд и ищет задачи, которые не обработаны и обрабатывает. И по выполнению ставит флаг в бд, что задача выполнена.
}
Все вроде ничего.
Проблема возникает если запустить несколько воркеров(несколько демонов). И пока один возьмет эту задачу и будет её обрабатывать, то другой воркер тоже её возьмёт и они вместе будут её обрабатывать. Что собственно так не должно быть.
БД MongoDB, так как весь проект на ней. Но для этой задачи можно и другую поставить. Тот же Redis, может у него есть специальные механизмы, что бы решить данную проблему?
Ну или может как то по другому организовать очередь?
German, RabbitMQ не хочется ставить.
Может знаете какую нибудь проверенную компоненту на гитхабе с решением подобного?
QNA-1976, хватит уже воспринимать "отправить письмо" как рассылку спама. Есть море задач, где нужно отправлять письма, вовсе не ставя задачи поспамить. Например, пусть условный сайт типа toster.ru посылает письма-уведомления всем, кто подписался на обновления вопроса, тогда на каждую операцию с вопросом (изменение формулировки, ответы, комментарии итд) будет ставиться задание на рассылку писем (быстро, без реальной отправки), а отдельный процесс будет эти письма реально отправлять.
QNA-1976, у автора был конкретный вопрос. Его не интересовало, что в твоих представлениях в электронной почте бывает только спам и не бывает нужных кому-то писем. Да и рассылку писем он привёл лишь для примера, вместо неё могла быть печать документов или какие-нибудь сложные вычисления.
QNA-1976, это ты уже додумал, что рассылка - это рассылка спама. Автор ничего подобного не говорил, и твоё "или" (в твоём понимании XOR, а не OR) тут совершенно неуместно.
Самый простой и по рабоче-крестьянски наивный способ - создать один общий для всех воркеров файл и ставить на него эксклюзивную блокировку (flock LOCK_EX) в момент взятия из очереди. Например, пусть у задачи есть поле proc_status, которое имеет значения NEW, RUNNING, FINISHED, FAILED. Тогда ставим блокировку, берём из базы задание в статусе NEW, делаем update set proc_status='RUNNING' и снимаем блокировку - никто другой одновременно с нами ту же задачу не возьмёт. Финальный статус можно ставить без блокировки.
Вместо блокировки файла можно использовать блокировки в самой базе, если они там есть (с Mongo не имел дела).
Ещё один не менее простой способ - распределять задачи по идентификаторам. Например, пусть у нас N воркеров, тогда пусть воркер k (где k=0...N-1) обрабатывает только те задания, где id%N=k.