【问题标题】:Sidekiq worker running for thousands of seconds even though there is a timeout即使超时,Sidekiq 工作程序也会运行数千秒
【发布时间】:2014-05-15 22:22:28
【问题描述】:

我有一个 sidekiq 工作者,它不应该花费超过 30 秒,但几天后我会发现整个工作者队列停止执行,因为所有工作者都被锁定了。

这是我的工人:

class MyWorker
  include Sidekiq::Worker
  include Sidekiq::Status::Worker
  sidekiq_options queue: :my_queue, retry: 5, timeout: 4.minutes

  sidekiq_retry_in do |count|
    5
  end

  sidekiq_retries_exhausted do |msg|
    store({message: "Gave up."})
  end

  def perform(id)
    begin
      Timeout::timeout(3.minutes) do
         got_lock = with_semaphore("lock_#{id}") do
           # DO WORK
         end
      end
    rescue ActiveRecord::RecordNotFound => e
      # Handle
    rescue Timeout::Error => e
      # Handle
      raise e
    end
  end

  def with_semaphore(name, &block)
    Semaphore.get(name, {stale_client_timeout: 1.minute}).lock(1, &block)
  end
end

还有我们使用的信号量类。 (redis-semaphore gem)

class Semaphore
  def self.get(name, options = {})
    Redis::Semaphore.new(name.to_sym,
      :redis => Application.redis,
      stale_client_timeout: options[:stale_client_timeout] || 1.hour,
    )
  end
end

基本上我会停止工作人员,它会声明完成:10000 秒,工作人员永远不应该运行。

有人对如何解决此问题或导致此问题的原因有任何想法吗?工人们正在 EngineYard 上运行。

编辑:一个额外的评论。 # DO WORK 有机会触发 PostgresSQL 函数。我注意到日志中提到了 PG::TRDeadlockDetected: ERROR: deadlock detected。即使设置了超时,这是否会导致工作人员永远无法完成?

【问题讨论】:

  • 您在工作人员的“执行”方法中使用信号量锁有什么具体原因吗?我问这个是因为我觉得 Sidekiq+Locking 的这种组合有点危险。 Sidekiq folks recommend 作业尽可能隔离,因此在#perform 期间不会因锁定内容而发生瓶颈或潜在死锁
  • 结合sidetiq我注意到有时相同的id会被扔到队列中进行处理,这可以防止它被处理两次。

标签: ruby-on-rails ruby-on-rails-3 redis semaphore sidekiq


【解决方案1】:

鉴于您想确保唯一的作业执行,我会尝试删除所有锁并将作业唯一性控制委托给像 Sidekiq Unique Jobs 这样的插件

在这种情况下,即使 sidetiq 两次将相同的作业 id 入队,此插件也会确保将其入队/处理一次。

【讨论】:

  • 那么你认为问题出在 redis-semaphore 上吗?我仍然不明白为什么超时会被忽略。
【解决方案2】:

你也可以试试 ActiveRecord with_lock 机制:http://api.rubyonrails.org/classes/ActiveRecord/Locking/Pessimistic.html

【讨论】:

  • 我其实怀疑这可能是问题的真正原因:blog.headius.com/2008/02/…
  • 啊,是的,看起来很像。注意自我,不要因为数据库锁而超时。您是否有理由需要它超时?也许有一种方法可以分解逻辑以允许访问中间的记录。
  • 顺便说一句,你应该考虑回答你自己的问题。
【解决方案3】:

我之前也遇到过类似的问题。要解决这个问题,您应该停止使用 Timeout。

正如this article 中所述,您永远不应该在 Sidekiq 作业中使用 Timeout。如果使用 Timeout,Sidekiq 进程和线程很容易中断。

不仅 Ruby,Java 也有a similar problem。无论使用哪种语言,从外部停止线程本质上都是危险的。

如果删除 Timeout 后仍然出现同样的问题,请检查您是否在代码中不小心使用了线程。

由于 Sidekiq 的架构如此复杂,几乎在所有情况下,错误的根源都在 Sidekiq 之外。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-02-14
    • 2015-02-05
    • 1970-01-01
    • 2015-09-13
    • 2021-01-03
    • 1970-01-01
    相关资源
    最近更新 更多