【问题标题】:In a Rails app running on multiple servers, how can I make sure a rake task runs only once?在多台服务器上运行的 Rails 应用程序中,如何确保 rake 任务只运行一次?
【发布时间】:2020-03-07 06:47:57
【问题描述】:

我有一个 Rails 应用程序在生产中的 2 台服务器上运行。

我有一个 rake 任务,它运行一些导入,然后向用户发送一封电子邮件。由于有 2 台服务器,我现在收到 2 封电子邮件。

为了避免这种情况,我尝试创建一个表my_import_run

  1. 我的导入将检查表中是否存在具有当前日期的记录,只有当不存在时才会运行导入并发送电子邮件。
  2. 发送电子邮件后,表中的所有记录将被删除,并会添加一条只有当前日期的记录

上面的算法实现如下:

def self.import(search_date = Date.current)
  if 0 == MyImportRun.count || !MyImportRun.where(:last_run => Date.today).any?
    # the code to run import job and send email goes here
    #
    #
    #
    #
    MyImportRun.delete_all
    MyImportRun.new(:last_run => Date.today)
    MyImportRun.save    
  end

但即使在执行上述代码后,我仍然收到 2 封电子邮件 - 我不知道为什么。我想知道两个导入可能同时运行,因此它们都找不到当前日期的记录并运行导入、发送电子邮件并删除所有记录并添加当前日期的记录。

有没有避免这种情况发生的干净方法?据我了解,rake 任务将自动导入 Linux 中的 cron 作业;所以我不能只从一台服务器上删除 rake 文件。

我确信必须有一个干净的方法来避免这种重复。

【问题讨论】:

  • 您使用什么方式在服务器上运行任务?什么时候宝石?
  • 没错。我每次使用 gem。
  • 如果你愿意改变“cron”平台,你可以使用arask。它只会运行一次任务。

标签: ruby-on-rails rake


【解决方案1】:

如果您使用WheneverCapistrano,可以使用roles 完成: 在部署文件中,您可以设置角色列表:

server 'first-server', user: 'deployer', roles: %w{app db web sidekiq job}, primary: true
server 'second-server', user: 'deployer', roles: %w{app db web sidekiq job2}

schedule.rb:

every 1.day, at: '5:00am', roles: [:job] do # this task will be run only on the first server
  rake "-s sitemap:refresh:no_ping"
end
every 1.day, at: '5:00am', roles: [:job2] do # this only on second
  rake "another_rake_task"
end

更多详情您可以找到this

【讨论】:

  • 非常感谢您的回复。我看到了你分享的链接——不过我在primary: true 上找不到任何信息。能否请您说明一下 primary: true 到底是做什么的?
  • primary: true 是解决我问题的关键吗?
  • 我不确定,但似乎 primary 服务器用于运行迁移
  • 非常感谢您的帮助 - 我发现这个链接提供了我需要的信息(示例部分) - makandracards.com/makandra/…
  • 我会在我的生产环境中尝试这个建议,并在几天后将您的回复标记为正确答案。再次感谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多