【问题标题】:Rails 4 Concurrency IssueRails 4并发问题
【发布时间】:2016-07-02 16:50:43
【问题描述】:

我很难弄清楚我的模型为什么会这样。

它有 2 个动作,cancelfeature,当属性已经被取消时,它不应该被推荐。

为了确保 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


【解决方案1】:

当 active == true 且 features == false 时,您可以调用功能操作。这将设置为 true,因为 active == true。

然后您可以调用取消操作。由于 features == true,action 设置为 false。

现在,活跃 == false 和特色 == true。

我画了一个简单的状态图来显示所有可能的状态变化。我没有包括无操作状态更改,例如,当 active 和 features 都为 false 时,您可以调用功能或取消操作,但这不会做任何事情。

箭头都表示调用 update_attributes 的路径。圆圈代表可以找到记录的不同状态。

可能不会出现活动和特色都为假的状态——我看不到记录是如何构建的,因此为了完整性我将其包含在内。

很容易看出记录是如何成为特色的,但不是活跃的。

编辑:

这是一个更新后的状态图,您的 if 语句已得到修复和简化:

所以好像不能取消,那就功能吧。

我想到的一件事是您没有检查 update_attributes 的返回值以查看更新是否成功。更新失败能否解释观察到的行为?

【讨论】:

  • 该死的,我问错问题了!取消条件是否定的:if !record_to_cancel.reload.featured?对此我深表歉意,问题已经更新!
【解决方案2】:

好的,我想我终于找到了问题。

在 redix-mutes 上,我应该使用非阻塞模式来避免这些问题。

这样,如果无法在第一个实例中进行锁定,该过程将被中止并且不会产生任何问题。

我有 id 的方式(阻止 30 秒)它尝试了 30 秒(轮询间隔为 100 毫秒)来建立锁定,并且以某种方式可能会导致像这样的多个问题。示例:两个线程试图获取一条记录的锁,最后一个线程首先建立它。

锁成功后lock方法返回true 已获取,或在几秒钟后尝试失败时返回 false 用 :block 指定。当 0 给 :block 时,它被设置为 非阻塞模式,立即返回false。

https://github.com/kenn/redis-mutex

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-02-11
    • 2023-03-16
    • 1970-01-01
    • 1970-01-01
    • 2015-08-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多