我有一个复杂的系统,涉及许多Resque工人,工作和监控过程。这些作业具有父子依赖关系,它们通过一系列状态(使用state-machine)运行,这是监视过程的原因。我们依赖于数据库状态来确保跨进程跟踪同步。
这是一个粗略的想法:
class ParentMonitor < ActiveRecord::Base
has_many children, class: ChildMonitor
state_machine :state, initial: :work_needed do
event :succeed do
transition :work_needed => :work_succeeded
end
event :fail do
transition :work_needed => :work_failed
end
end
def child_transition
return if children.any? { |child| child.work_needed? }
if children.any? { |child| child.work_succeeded? }
succeed
else
fail
end
end
end
class ChildMonitor < ActiveRecord::Base
belongs_to: owner, class: ParentMonitor
state_machine :state, initial: :work_needed do
event :succeed do
transition :work_needed => :work_succeeded
end
after_transition :to => :work_succeeded, :do => :notify_owner
event :fail do
transition :work_needed => :work_failed
end
after_transition :to => :work_failed, :do => :notify_owner
end
def notify_owner
owner.child_transition
end
end
正在发生的事情是,对于前几个这样的工作(比如几百个中的十几个),即使所有孩子都在work_needed
,所以ParentMonitors仍处于work_succeeded
状态。或work_failed
。通过跟踪和测试,我确定每次调用ParentMonitor#child_transition
时,处于“需要工作”状态的子项列表已相继减少,直到某些时候它会使数据库加载并用值替换所有子项“需要的工作”。尽管有些人以前已经完成了。
此外,我没有在这些前几个孩子的日志文件中看到任何UPDATE
日志,直到它突然开始记录更新。当它似乎重置所有孩子的状态时,该记录是同步的。
这让我觉得由于某些缓存状态,所有更改都在内存中发生,但我在整个过程中添加了reload
,save
和find
个调用似乎会改变。我也尝试在uncache
中包装这些调用,但这没有用。
答案 0 :(得分:0)
事实证明,这是因为写入是在长时间运行的事务中保存的,因为状态机gem保持在状态更改和任何after
挂钩结束之间打开一个事务。我们编写了在主监视循环上运行了几个小时的钩子。
我们通过在状态更改之间而不是在回调中执行操作来解决此问题。
顺便提一下,错误的行为完全如最新的Red Book所描述的那样,是弱隔离的副作用&#34;在大多数RDMBS中实现并发:
示例异常包括读取另一个事务产生的中间数据,读取中止的数据,在执行同一事务期间读取同一项的两个或更多不同值,以及“丢失”由于并发写入事务而导致的某些事务影响项目