我的用户模型有一个讨厌的方法,不应该同时为同一记录的两个实例调用.我需要连续执行两个http请求,同时确保任何其他线程不会同时为同一记录执行相同的方法.
class User ... def nasty_long_running_method // something nasty will happen if this method is called simultaneously // for two instances of the same record and the later one finishes HTTP_Request_1 // before the first one finishes HTTP_Request_2. HTTP_Request_1 // Takes 1-3 seconds. HTTP_Request_2 // Takes 1-3 seconds. update_model end end
例如,这会破坏一切:
user = User.first Thread.new { user.nasty_long_running_method } Thread.new { user.nasty_long_running_method }
但这没关系,应该允许:
user1 = User.find(1) user2 = User.find(2) Thread.new { user1.nasty_long_running_method } Thread.new { user2.nasty_long_running_method }
解决方法
在为我的问题寻找解决方案时,我找到了一个宝石
Remote lock.它是一种在后端使用Redis的互斥解决方案.
它:
>可以访问所有进程
>不会锁定数据库
>在记忆中 – >快速而无IO
该方法现在看起来像这样
def nasty $lock = RemoteLock.new(RemoteLock::Adapters::Redis.new(REdis)) $lock.synchronize("capi_lock_#{user_id}") do HTTP_Request_1 HTTP_Request_2 update_user end end