Rails 4 并发问题
Rails 4 Concurrency Issue
我很难弄清楚我的模型出现这种行为的原因。
它有 2 个操作,取消 和 特征,当属性已经被取消时,它不应该被推荐。
为了确保 功能 不会对已取消的属性执行,我使用 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
我想了解为什么有时(很少见)属性会先被取消然后再成为特色 - 换句话说:特征然后取消也可能发生只是我没有检测到它。
请告诉我这是否是一个糟糕的方法,如果是的话,什么是解决此问题的好方法。
当active == true 和featured == false 时,您可以调用feature 操作。这会将 featured 设置为 true,因为 active == true.
然后就可以调用取消动作了。由于 featured == true,action 设置为 false。
现在,活跃 == false 和精选 == true。
我画了一个简单的状态图来展示所有可能的状态变化。我没有包括无操作状态更改,例如,当 active 和 featured 都为 false 时,您可以调用功能或取消操作,但这不会做任何事情。
箭头都代表调用update_attributes的路径。圆圈代表可以找到记录的不同状态。
可能不会出现 active 和 featured 都为 false 的状态 -- 我看不到记录是如何构建的,因此为了完整性我将其包括在内。
很容易看出该记录如何成为特色,但尚未激活。
编辑:
这是更新后的状态图,其中修复并简化了 if 语句:
所以好像不能取消,然后功能
我想到的一件事是您没有检查 update_attributes 中的 return 值来查看更新是否成功。失败的更新可以解释观察到的行为吗?
好的,我想我终于找到问题了。
在redix-mutes上,我应该使用非阻塞模式来避免这类问题。
这样,如果无法在第一时间进行锁定,进程将中止并且不会产生任何问题。
我有 id(阻止 30 秒)的方式是尝试 30 秒(100 毫秒的轮询间隔)来建立锁定,并且不知何故可能会导致多个此类问题。示例:两个线程试图获取一条记录的锁,最后一个线程首先建立它。
The lock method returns true when the lock has been successfully
acquired, or returns false when the attempts failed after the seconds
specified with :block. When 0 is given to :block, it is set to
non-blocking mode and immediately returns false.
我很难弄清楚我的模型出现这种行为的原因。
它有 2 个操作,取消 和 特征,当属性已经被取消时,它不应该被推荐。
为了确保 功能 不会对已取消的属性执行,我使用 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
我想了解为什么有时(很少见)属性会先被取消然后再成为特色 - 换句话说:特征然后取消也可能发生只是我没有检测到它。
请告诉我这是否是一个糟糕的方法,如果是的话,什么是解决此问题的好方法。
当active == true 和featured == false 时,您可以调用feature 操作。这会将 featured 设置为 true,因为 active == true.
然后就可以调用取消动作了。由于 featured == true,action 设置为 false。
现在,活跃 == false 和精选 == true。
我画了一个简单的状态图来展示所有可能的状态变化。我没有包括无操作状态更改,例如,当 active 和 featured 都为 false 时,您可以调用功能或取消操作,但这不会做任何事情。
箭头都代表调用update_attributes的路径。圆圈代表可以找到记录的不同状态。
可能不会出现 active 和 featured 都为 false 的状态 -- 我看不到记录是如何构建的,因此为了完整性我将其包括在内。
很容易看出该记录如何成为特色,但尚未激活。
编辑:
这是更新后的状态图,其中修复并简化了 if 语句:
所以好像不能取消,然后功能
我想到的一件事是您没有检查 update_attributes 中的 return 值来查看更新是否成功。失败的更新可以解释观察到的行为吗?
好的,我想我终于找到问题了。
在redix-mutes上,我应该使用非阻塞模式来避免这类问题。
这样,如果无法在第一时间进行锁定,进程将中止并且不会产生任何问题。
我有 id(阻止 30 秒)的方式是尝试 30 秒(100 毫秒的轮询间隔)来建立锁定,并且不知何故可能会导致多个此类问题。示例:两个线程试图获取一条记录的锁,最后一个线程首先建立它。
The lock method returns true when the lock has been successfully acquired, or returns false when the attempts failed after the seconds specified with :block. When 0 is given to :block, it is set to non-blocking mode and immediately returns false.