【问题标题】:Overlapping cron job that runs the same Django management command: problematic?运行相同 Django 管理命令的重叠 cron 作业:有问题吗?
【发布时间】:2016-02-25 23:24:52
【问题描述】:

我有一个运行 Django 管理命令的定期 cron 作业。该命令与 ORM 交互,使用 sendmail 发送电子邮件,并使用 Twilio 发送 SMS。 cron 作业可能会开始重叠。换句话说,当下一个作业开始运行时,作业(运行此命令)可能仍在执行。这会导致任何问题吗? (我不想等到管理命令执行完毕后再用 cron 再次运行管理命令)。

编辑:

管理命令的开头获取命令运行时间的时间戳。至少,此时间戳需要准确。如果命令的其余部分不等待前一个 cron 作业完成运行,那就太好了,但这并不重要。

编辑 2:

cron 作业只从数据库中读取,不写入。应用程序必须在 cron 作业运行时继续工作。应用程序从数据库中读取和写入。

【问题讨论】:

  • 你可以使用flock来达到这个目的。

标签: python django cron manage.py


【解决方案1】:

所以你有一个运行 django 管理命令的 cron 并且你不希望它们重叠。

您可以使用 flock,它会生成一个锁定文件并在执行 cron 后将其删除。如果第二个 cron 在第一个 cron 结束之前启动,它将看到已经创建了一个锁定文件,因此没有执行第二个。

下面是我使用的cron:

* * * * * /usr/bin/flock -n /tmp/fcj.lockfile /usr/bin/python /home/txuser/dev/Project1/projectnew/manage.py flocktest

你可以用它做更多的事情。 more on this

【讨论】:

    【解决方案2】:

    是的。这肯定会导致问题。你有a race condition。如果您愿意,您可以以某种方式在critical section 上获得锁,这将阻止下一次调用进入一段代码,直到第一次调用命令完成。您也许可以对基础数据进行行锁或表锁。

    假设您使用的是具有特定锁定语法(依赖于数据库)的 MySQL,并且您有这个模型:

    class Email(models.Model):
        sent = models.BooleanField(default=False)
        subj = models.CharField(max_length=140)
        msg = models.TextField()
    

    你可以像这样创建一个锁对象:

    from django.db import connection
    [...]
    class EmailLocks(object):
        def __init__(self):
            self.c = connection.cursor()
        def __enter__(self):
            self.c.execute('''lock tables my_app_email write''')
        def __exit__(self, *err):
            self.c.execute('unlock tables')
    

    然后锁定所有您的关键部分,例如:

    with EmailLocks():
        # read the email table and decide if you need to process it
        for e in Email.objects.filter(sent=False):
            # send the email
            # mark the email as sent
            e.sent = True
            e.save()
    

    锁定对象将在退出时自动解锁表。此外,如果您在代码中抛出异常,该表仍将被解锁。

    【讨论】:

    • 那么问题是代码的哪些部分需要锁定?关于如何找到答案的任何想法?
    • 您需要锁定cricital section。除非您发布所有管理代码,否则我不知道那会是什么。可能会猜到您有一张要发送消息的表格,您阅读该表格,发送消息,然后将消息标记为已发送。您必须在阅读之前锁定表格,然后在将消息标记为已发送后解锁。
    • 如果您无法锁定写入或读取,并且只有您的管理进程写入“发送”位(再次假设),那么您可以显式创建一个虚拟表用于锁定。您将在进入临界区之前锁定此代理/虚拟表,然后在更新真实表后解锁它。
    • 您使用的是什么数据库?我可以教你如何锁定。
    • Postgresql,最新稳定版本。谢谢。我想我必须使用虚拟表的想法。只是为了检查一下,在管理命令开始时运行 nowdate = datetime.datetime.now(pytz.timezone('UTC')) 是否算作关键部分的一部分?我猜不是。
    【解决方案3】:

    我对 cron 的理解是,它将一个作业分叉为后台进程,允许多个作业同时运行。如果第二项工作依赖于要完成的第一项工作(如果第二项工作正在运行第一项工作提供的汇总数据的每日报告等),这可能会出现问题。如果您不希望它们同时运行,有一些解决方法:

    How to prevent the cron job execution, if it is already running.

    Will Cron start a new job if the current job is not complete?

    【讨论】:

    • 请看我的编辑。至少它的一部分不是必须同时运行吗?
    • 如果您询问它们是否可能在任何时候同时运行?是的,它可能,取决于执行时间的长度和 cron 的频率。如果您询问它们是否有必要同时运行,我会说不,如果它们出于某种原因确实需要同时运行,我会说它们应该被重构为 1 个脚本,以确保两个逻辑块都按顺序执行。希望这会有所帮助,不要对您实际发生的事情有太多了解。
    猜你喜欢
    • 1970-01-01
    • 2021-07-05
    • 1970-01-01
    • 2021-08-13
    • 1970-01-01
    • 2013-07-24
    • 2012-05-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多