修改现有的时区初始调度程序以处理夏令时



我们目前在纯python中有一个时区不感知的调度器。

它使用有序事件的堆(python二进制堆),包含时间,回调和回调参数。 它从堆中获取最小值时间,计算事件发生前的秒数,并在运行作业之前休眠该秒数。

我们不需要担心计算机被挂起;这是在专用服务器上运行,而不是笔记本电脑。

我们希望调度程序能够很好地应对时区更改,这样我们在 11 月就不会像最近那样遇到问题(我们有一个重要的工作必须在数据库中进行调整,使其在上午 8:15 而不是上午 9:15 运行 - 通常它在上午 8:15 运行)。 我想我们可以:

  1. 以 UTC 格式存储所有时间。
  2. 使调度程序休眠 1 分钟,并在循环中测试重新计算每次都"现在",并与作业日期时间进行 <= 比较。
  3. 作业运行频率超过每小时一次,应该"正常运行"。
  4. 在凌晨 2:
  5. 00 和凌晨 2:59(含)之间运行的每小时作业时间变化日,可能应该跳过一个小时进行 PST->PDT,然后运行PDT->PST 的额外时间。
  6. 作业运行时间少于每小时可能应避免在任一情况下重新运行在有时间变化的日子里的情况。

这听起来对吗? 它在哪里可能关闭?

谢谢!

我之前写过几次关于其他编程语言的调度。 这些概念也适用于python。 您可能希望阅读其中一些帖子: 1, 2, 3, 4, 5, 6

我将尝试从Python的角度再次解决具体问题:

  • 单独的定期模式与执行时间分开非常重要。 定期模式应存储用户输入的时间,通常是本地时间。即使重复模式是"只有一次",也应将其存储为本地时间。 调度是少数几个用例之一,其中"始终以 UTC 工作"的常见建议站不住脚!

  • 您还需要存储时区标识符。 这些应该是 IANA 时区,例如 America/Los_AngelesEurope/London 。 在 Python 中,您可以使用 pytz 库来处理这样的时区。

  • 执行时间确实应该基于 UTC。 任何事件的下一个执行时间都应从定期模式中的本地时间计算。 您可能希望提前计算和存储这些执行时间,以便可以轻松确定接下来要运行的事件。

  • 您应该准备好重新计算这些执行时间。 您可能希望定期执行此操作,但至少应在对系统应用时区更新时执行此操作。 您可以(并且应该)订阅 IANA 的 tz 更新公告,然后在 pypi 上查找相应的 pytz 更新。

    这样想吧。 当您将本地时间转换为 UTC 时,您假设您知道该时间点的时区规则是什么,但没有人可以预测政府将来会做什么。 时区规则可以更改,而且经常更改。 您需要考虑到这一点。

  • 你应该测试无效和模棱两可的时间,并有一个处理它们的计划。 这些在计划时很容易命中,尤其是对于重复发生的事件。

    例如,您可以将任务安排在每天凌晨 2:00 运行,但在春季前向转换的当天,该时间不存在。 那你应该怎么做? 在许多情况下,您需要在当天的凌晨 3:00 跑步,因为这是凌晨 1:59 之后的下一次。 但在某些(罕见的)上下文中,您可能会在凌晨 1:00 或凌晨 1:59 运行,或者完全跳过当天。

    同样,您可以将任务安排为每天凌晨 1:00 运行,但在回退转换当天,凌晨 1:00 会发生两次。 那你怎么办? 在许多情况下,第一个实例(即日光实例)是开火的正确时间。 在其他(罕见)情况下,第二个实例可能更合适,或者(甚至更罕见)实际运行作业两次可能更合适。

对于按every X [hours/minutes/seconds]类型计划运行的作业:

  • 这些最容易按 UTC 计划,并且不应受 DST 更改的影响。

  • 如果这些是您正在运行的唯一作业类型,则可以将整个系统基于 UTC。 但是,如果您混合运行不同类型的作业,则可以考虑在定期模式中将"本地时区"设置为"UTC"。

  • 或者,您可以按真正的本地时间安排它们,只需确保在作业运行时,它会根据当前执行时间(应该已经采用 UTC)计算下一个执行时间。

  • 不应区分每小时运行超过小时的作业或每小时运行少于小时的作业。 我预计在回退过渡当天每小时运行 25 次,在春季前向过渡当天运行 23 次。

关于你每分钟循环睡觉和醒来一次的计划 - 只要你没有亚分钟的任务要处理,这可能会奏效。 不过,这可能不一定是最有效的处理方式。 如果您正确地预先计算并存储了执行时间,则可以将单个任务设置为在下次唤醒以运行,运行需要运行的所有内容,然后为下一个执行时间设置一个新任务。 您不一定每分钟醒来一次。

您还应考虑运行计划作业所需的资源。 如果您安排了 1000 个任务,这些任务都需要在午夜运行,会发生什么情况? 好吧,它们不一定能够在一台计算机上同时运行。 您可以将它们排队以批量运行,或者将负载分散到不同的时间段。 在云环境中,也许您需要启动额外的工作线程来处理负载。

最新更新