【问题标题】:delayed_job: One job per tenant at a time?delay_job:每个租户一次一份工作?
【发布时间】:2019-03-11 21:46:37
【问题描述】:

我有一个多租户 Rails 应用程序,其中包含多个 delay_job 工作人员。

为了避免重叠租户特定的工作,我想将工作人员彼此分开,使每个工作人员一次只处理一项特定于租户的任务。

我考虑过使用(命名的)queue 列并添加“tenant_1”、“tenant_2”等。不幸的是,队列必须在配置期间命名,因此该原则对于许多租户来说不够灵活。

有没有办法自定义delayed_job 选择下一个任务的方式?还有其他方法来定义范围吗?

【问题讨论】:

  • 这是否意味着您需要为系统中的每个租户至少启动 1 个工作人员?除非每个租户也拥有自己的服务器,否则这听起来不太可扩展。每个工作人员都将整个 Rails 应用程序保存在内存中。
  • @Unixmonkey 只有正在运行的工作人员才会消耗内存,对吧?不是预定的。
  • 我试图让一群工人同时只为不同的租户工作。
  • 调度作业只是意味着序列化运行作业所需的数据并将其添加到delayed_jobs 表中,您通常仍然有一个空闲的工人池等待接作业并处理它们。如果您在安排工作的同时解雇了一名工作人员,并且在完成时让它自行终止,我也许可以看到这个工作,但这仍然是很多开销,并且您可能必须重新实现自己的重试逻辑.

标签: ruby-on-rails scope multi-tenant delayed-job


【解决方案1】:

您最好的选择可能是旋转实现分布式锁的自定义解决方案 - 基本上,工作人员都正常运行并从通常的队列中拉出,但在与另一个系统(Redis、RDBMS、API 等)执行工作检查之前验证没有其他工作人员尚未为该租户执行工作。如果该租户没有工作,则为相关租户设置锁定并工作。如果租户被锁定,请不要执行该工作。这是您对许多实现细节的调用,例如是否继续尝试另一个作业、将作业重新排入队列后面、是否将其视为失败并将其绑定到您的重试限制,或者做其他事情完全。这是非常开放的,所以我将把细节留给你,但这里有一些提示:

  • 继承将是你的朋友;在基础作业上定义此行为,并在您希望工作人员运行的作业上继承它。这还允许您自定义行为,如果您有某些工作的“特殊”情况,而不会破坏其他一切。
  • 假设您没有通过 ActiveJob 运行(因为它没有被提及),请阅读 delayed_job 挂钩:https://github.com/collectiveidea/delayed_job/#hooks - 它们可能是一个合适和/或有用的工具
  • 熟悉悲观和乐观锁定策略的一些差异和权衡 - 这个答案是一个很好的起点:Optimistic vs. Pessimistic locking
  • 阅读有关分布式锁概念的一般实践,以便您可以为自己选择最佳工具和策略(不一定是疯狂复杂的解决方案,数据库中存储租户标识符的简单表是足够了,但您需要考虑失败案例 - 例如,如何管理被放弃的锁)

认真考虑不要这样做;系统正常运行真的有严格要求吗?如果是这样,这可能表明您的数据模型存在潜在缺陷,或者您如何围绕该数据进行结构化转换。在考虑对数据进行操作时,在应用程序中争取 ACIDity,这样可以避免很多此类问题。它不是后台作业运行器中常用的“开箱即用”功能是有原因的。如果存在潜在缺陷,它不仅会在这个问题上咬你,还会在其他问题上咬你——保证!

【讨论】:

  • 谢谢!我不知道延迟工作挂钩效果很好!我的应用程序的缺陷是我使用的 API 只允许每个租户一个连接。
【解决方案2】:

如果您试图避免两个不同的工作人员在同一个租户上工作,那么这是一个糟糕的设计选择。有东西在闻。先解决这个问题。但是,如果您希望相同类型的工作实例在不同的租户上工作,那么下面是最简单的解决方案。这些关系是我的假设。

ExpiredOrderCleaner = Struct.new(:tenant_id) do
  def perform
    Order.where(tenant_id: tenant_id).expired.delete_all
  end
end

Tenant.each do |tenant|
  Delayed::Job.enqueue ExpiredOrderCleaner.new(tenant.id)
end

这将为每个租户创建独特的工作。单个工作实例将在特定租户上工作。但是,同一租户可能有其他类型的工作。这应该是好的。如果您需要更小的范围,只需为工作人员传递更多参数并在查询中使用并使用数据库事务来避免冲突。

这些best practices 适用于任何后台工作人员。

  • 让你的工作具有幂等性和事务性 意味着你的工作可以安全地执行多次
  • 拥抱并发 设计您的工作,以便您可以并行运行许多工作

如果您使用apartment gem 和active job 包装器,您的工作会轻松很多。请参阅那里的文档中的示例。

【讨论】:

    猜你喜欢
    • 2021-05-14
    • 2013-11-23
    • 2015-07-30
    • 1970-01-01
    • 2017-01-20
    • 2020-08-03
    • 1970-01-01
    • 2014-11-01
    • 1970-01-01
    相关资源
    最近更新 更多