Сегодня столкнулся с проблемой: часть задач отправлялась в celery, но не выполнялись, причем в rabbitmq-очередях была пустота – т.е. задачи были уже забраны в celery но не были выполнены, а в таблице celery_taskmeta эти задачи имели состояние PENDING.
Все дело оказалось в том, как celery раздает задачи child-процессам. По-умолчанию, задачи раздаются по-очереди, поэтому, например если у Вас есть celery-worker с двумя паралельными child-процессами, то задачи будут розданы им “по кругу” (а точнее, по round-robin):
Причем, такой алгоритм будет применен независимо от того, свободен дочерний процесс или занят. А это означает, что если один из двух дочерних процессов будет занят бесконечно долгой задачей, то 50% отправленных задач вообще никогда не выполнятся, в то время как второй дочерний процесс выполнив свою половину задач будет простаивать.
Для обхода такого поведения celery имеет опцию “-Ofair”. Подробное описание опции находится здесь.
После запуска worker-ов с данной опцией раздача задач будет происходить по принципу “в следующий свободный обработчик”, а это в свою очередь очень полезно тогда, когда у вас есть “долгие” задачи.