То есть. скажем, есть "крон", который запускается раз в минуту, но по какой-то причине, предыдущий таск работает больше минуты, как сделать так, чтобы новый таск не создавался, а ждал окончания работы предыдущего?
Правильным решением будет использовать распределённые локи. При запуске таски брать лок. Если взялся - работаем дальше. Иначе завершаем работу. Реализация локов зависит от того, что у вас есть. Можно в Redis, можно наверное как-то в обычных RDB сделать, или использовать такие штуки как etcd или consul.
А можно просто периодику делать не средствами селери
Можно ещё выделить под периодику отдельную очередь, и запускать воркер для этой очереди только на одном сервере, и только в одном экземпляре. Но это совсем не HA и не масштабируется. Плюс если таска будет всегда работать больше периода её запуска, то очередь будет забиваться этими тасками.
Ну это как работает селери примерно. Нафиг. А я про средства k8s
Ну да - нафиг Celery, давайте кубер развернём 😊. Надо больше хаоса и боли в маленькой системе, лишь бы самим не делать распредлённые локи.
В кубере как раз etcd, вроде, который наверняка используется в том числе и для "локов".
ну это уже детали ег ореализации, ты ж не руками будешь блокироваться, а просто periodic job заведешь
ну если нету, не надо заводить ради этого. Я ж не спорю.
я так и сделал, то есть в БД пишу задачу, которую запустил, и при завершении ставлю метку об этом, и при последующем запуске проверяю, завершился ли таск или нет.. но мне кажется это таким...мм странным
Ну тут вполне редисного ключ:значение имхо достаточно
Это не странно - это нормально. В большинстве систем, которые обрабатывают задачи параллельно рано или поздно потребуются распределённые локи. Сделать полностью lock-free систему - задача очень не простая. Где-то роль локов, по началу, может сыграть база данных, которая блокирует строки, которые будут обновляться в "таске". Но такое не всегда может подойти (например когда не надо ждать освобождения лока, а просто завершить работу). Поэтому лучше просто взять нормальное, быстрое решение для локов и не изобретать велосипедов.
Ни разу не юзал распределеные локи. Обхожусь очередями
Это помогает только если у очереди ровно один воркер.
Про партиционирование слышал?
Ну как вам поможет очередь, если из неё смогут доставать таски одновременно несколько воркеров?
Каждый воркер будет читать из своей партиции
Ну значит у вас ровно один воркер на каждой партиции, и вам надо придумывать какой-то способ распределять задачи по партициям, что бы все конфликтующие попадали в одну и ту же. Не уверен, что это всегда легко сделать.
простите, а как вы локи будете захватывать? Либо у вас один лок -> анало годной партиции, либо несколько и надо по задаче понимать какой лок юзать. Так же самая проблема
У меня локи нужны не только в воркерах, а ещё и в бекендах, которые обрабатывают HTTP-запросы. А раз они всё равно нужны - то проще их везде использовать и не парится патрицированием очередей. Особенно когда один и тот же лок может использоваться в таске и в обработчике HTTP-запроса.
Часто есть задача, особенно с крон-тасками, что если лок занят, то надо просто скипнуть копию таски, выкинуть её из очереди и двигаться дальше. Патрицирование как-то помогает это решить?
крон вообще через очередь не нужен. Если у вас время обработки больше чем время ожидания - нужен ли вам крон вообще? Просто делаешь вечно работащий сервис, который по кругу гоянет одно и то же. Умер - рестартанул и продолжил
Мне надо больше одного такого сервиса, что бы запускать в параллель не конфликтующие крон-таски. И что-бы они ещё напрасно память не занимали, а выполняли другие задачи из очереди.
ну значит тебе актуальны локи
А ещё у меня MongoDB без транзакций 😊 . Локи помогают в кейсах, когда надо внести связанные изменения в несколько коллекций или документов.
ну вот мы и нашли корень проблем
не обновлялись до 4.х?
интересно как тебе локи атомарность связанных изменений обеспечивают
Ну если бы были транзакции, то они бы не решили все проблемы на 100%. Например не рекомендуется держать долго открытые транзакции. Много таких открытых транзакций негативно сказываются на производительности базы данных.
Обновились, но для того что бы там работали транзакции - надо иметь реплики и не использовать транзакции на шардированных коллекциях. С первым в целом ОК на продакшен стендах, а вот со вторым условием не выходит. Монга без шардирования нужна только в спецефических кейсах.
это какие решение?
Ну с атомарностью конечно не помогают. Но случаи когда всё неожиданно ломается посередине процесса, случаются сильно реже, чем параллельный запуск кода, который вносит изменения в одни и те же документы.
Например локи через Redis. А если ещё нужна персистентность локов и их выживание при потере некоторых нод, тогда etcd, Сonsul, ZooKeeper и подобное Можно в принципе в MongoDB тоже локи сделать, но они будут явно медленнее чем в Redis
кажется вообще не в ту степь ушли =) etcd, zookeeper и тд это уже оверхед. я на втором проекте это решил через один воркер =)) пока он не освободится, задачи копятся и потом выполнятся
Ну если проект небольшой, и допустимо повторно гонять таску ещё раз после того как она уже выполнилась, и очередь не забивается надолго - почему бы и нет. У меня проект достаточно нагруженный, и если очередь будет тупить и затыкаться - она быстро наберёт от 10к до нескольких млн. сообщений. Поэтому несколько воркеров на очередь - это у нас обязательное условие.
Обсуждают сегодня