我有一个多租户-Rails应用程序,其中有多个延迟工作的工人。
为了避免重叠特定于承租人的工作,我想将工人彼此分开,使得每个人一次只能执行一个特定于承租人的任务。
我考虑过使用(命名)队列列,并添加“ tenant_1”,“ tenant_2”等。不幸的是,在配置过程中必须对队列进行命名,因此对于许多租户来说,这种原理不够灵活。
有没有一种方法可以自定义delay_job选择下一个任务的方式?还有另一种定义范围的方法吗?
答案 0 :(得分:2)
您最好的选择可能是旋转一个实现分布式锁的自定义解决方案-本质上,工作人员都可以正常运行并从通常的队列中拉出,但是在与另一个系统(Redis,RDBMS,API等)进行工作检查之前确认没有其他工人正在为该租户执行工作。如果该租户没有工作,则为有问题的租户设置锁,然后进行工作。如果租户被锁定,请勿执行该工作。您需要调用许多实现细节,例如是否继续尝试其他作业,将作业重新排入队列的后面,是否将其视为失败并将其绑定到重试限制或执行其他操作完全。这是开放式的,因此我将详细信息留给您,但是这里有一些提示:
delayed_job
钩子:https://github.com/collectiveidea/delayed_job/#hooks-它们可能是合适和/或有用的工具认真考虑不要这样做;系统正常运行真的需要严格吗?如果是这样,则可能表明您的数据模型存在潜在的缺陷,或者表明您如何围绕该数据构建转换。考虑数据操作时,努力在应用程序中使用ACIDity,这样可以避免很多此类问题。有一个原因是它不是后台求职者通常无法使用的“开箱即用”功能。如果存在潜在的缺陷,它不仅会在这个问题上咬您,而且还会在其他方面咬您-保证!
答案 1 :(得分:0)
如果您要避免让两个不同的工人在同一个租户上工作,那么这是一个糟糕的设计选择。有东西在闻。首先解决。但是,如果您希望下面的相同类型的工作实例在不同的租户上工作是最简单的解决方案。这些关系是我的假设。
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
这将为每个租户创建唯一的作业。单个工作人员实例将在特定租户上工作。但是,同一租户可以有其他类型的工作。这应该是不错的。如果您需要更小的作用域,只需为worker传递更多的参数,并在查询中使用,并使用数据库事务来避免冲突。
这些best practices对任何后台工作者都是正确的。
如果使用apartment gem和active job包装器,您的工作会容易得多。请参阅其中的示例文件。