【发布时间】: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