【发布时间】:2016-07-02 16:50:43
【问题描述】:
我很难弄清楚我的模型为什么会这样。
它有 2 个动作,cancel 和 feature,当属性已经被取消时,它不应该被推荐。
为了确保 feature 不会对已取消的属性执行,我使用Redis-Mutex 锁定属性以实现排他性并在块内进行适当的验证。这应确保在一侧处理的记录不会同时在另一侧处理:
功能操作:
mutex = RedisMutex.new(record_to_feature, block: 30, sleep: 0.1)
if mutex.lock
record_to_feature = record_to_feature.reload
if record_to_feature.reload.active?
record_to_feature.reload.update_attributes(featured: true)
end
mutex.unlock
end
取消操作:
mutex = RedisMutex.new(record_to_cancel, block: 30, sleep: 0.1)
if mutex.lock
record_to_cancel = record_to_cancel.reload
if !record_to_cancel.reload.featured?
record_to_cancel.reload.update_attributes(active: false)
end
mutex.unlock
end
我想了解为什么有时(罕见)属性首先被取消然后被推荐 - 另一种方式:特征然后取消也可能发生,只是我没有检测到它。
如果这是一个不好的方法,请告诉我,如果是,那么解决这个问题的好方法是什么。
【问题讨论】:
-
据我所知,不可能同时访问两个动作(互斥体中的问题),所以我怀疑是由于某种原因,当第二个动作被调用 /获取锁并读取属性(使用.reload)它们仍然由于某种原因(数据库提供的过时信息)没有更新,因此执行了第二个操作。问题是一样的……为什么会这样?
-
唯一有意义的事情是 30 秒后的超时(可能是当底层数据库长时间不可用时)或 RedisMutex 中的错误。虽然这些不会成为问题的原因,1) 除了每个方法中的第一个
reload之外,所有方法都在浪费性地查询数据库,而您已经拥有了锁。 2)我想知道你为什么不使用本机数据库:lock而不是redis? (也许您使用的数据库不支持行锁定?) -
哦,还有一件事:RedisMutex 锁键是类名和对象 id (github.com/kenn/redis-mutex/blob/master/lib/redis_mutex.rb#L20) 的字符串连接。如果您使用的是 AR 类层次结构(例如,对于单表继承),您最终可能会为同一底层表行使用不同的锁。
-
在仔细检查了日志和其他可用元素后,我可以看到上次出现此问题时,该元素在 6 秒前被取消,仅 6 秒后该功能被调用,得到重新-locked 然后成为特色,所以我认为整个问题不在于我在此强调的互斥锁和并发问题,而在于取消后它如何通过此条件:
if record_to_feature.reload.active? -
好的。这是伟大的侦探工作。正如我所说,除了每个互斥锁块中的第一个重载之外,所有重载都没有完成任何事情。照你说的做,你应该搜索系统以寻找在锁外发生的其他更新。
标签: ruby-on-rails ruby-on-rails-4 concurrency mutex