Django Celery: выполнить только один экземпляр долговременного процесса

У меня есть длительный процесс, который должен запускаться каждые пять минут, но несколько экземпляров процессов никогда не должны запускаться одновременно. Обычно процесс не должен запускаться через пять минут, но я хочу быть уверенным, что второй экземпляр не запустится, если он запустится.

В предыдущей рекомендации я использую Django Celery, чтобы запланировать эту долгосрочную задачу.

Я не думаю, что периодическая задача будет работать, потому что, если у меня есть пятиминутный период, я не хочу, чтобы вторая задача выполнялась, если другой экземпляр задачи уже запущен.

Мой текущий эксперимент выглядит следующим образом: в 8:55 начинается запуск экземпляра задачи. Когда задача заканчивается, она запускает другой экземпляр для запуска на следующей отметке в пять минут. Поэтому, если первая задача завершилась в 8:57, вторая задача будет работать в 9:00. Если первая задача выполняется длительно и заканчивается в 9:01, она будет планировать следующий экземпляр для запуска в 9:05.

Я борется с множеством критических ошибок, когда делаю что-то большее, чем простой пример ниже, и я не нашел других примеров людей, планирующих задачи из предыдущего экземпляра. Мне интересно, есть ли лучший способ сделать то, что я пытаюсь сделать. Я знаю, что есть способ назвать одну задачу; возможно, есть способ поиска запущенных или запланированных экземпляров с тем же именем? Есть ли у кого-нибудь какие-либо советы по поводу выполнения задачи каждые пять минут, но при условии, что одновременно выполняется только одна задача?

Спасибо, Джо

В mymodule/tasks.py:

import datetime
from celery.decorators import task 

@task 
def test(run_periodically, frequency):

    run_long_process()
    now = datetime.datetime.now()
    # Run this task every x minutes, where x is an integer specified by frequency
    eta = (
      now - datetime.timedelta(
         minutes =  now.minute % frequency , seconds = now.second, 
         microseconds = now.microsecond ) ) + datetime.timedelta(minutes=frequency) 
    task = test.apply_async(args=[run_periodically, frequency,], eta=eta)  

Из оболочки. /manage.py:

from mymodule import tasks
result = tasks.test.apply_async(args=[True, 5])

Ответ 1

Вы можете использовать периодические задачи в сочетании со специальной блокировкой, которая гарантирует выполнение задач по одному. Ниже приведен пример реализации документации Celery:

http://ask.github.com/celery/cookbook/tasks.html#ensuring-a-task-is-only-executed-one-at-a-time

Ваш описанный метод с заданием планирования из предыдущего выполнения может остановить выполнение задач, если в одном из них произойдет сбой.

Ответ 2

Мы используем Celery Once, и это решило подобные проблемы для нас. Ссылка на Github - https://github.com/cameronmaske/celery-once

Он имеет очень интуитивно понятный интерфейс и проще в использовании, чем рекомендованный в документации сельдерея.