如何确保ActiveRecord正在保存对数据库的更改

时间:2014-05-20 01:21:53

标签: ruby-on-rails ruby activerecord resque

我有一个复杂的系统,涉及许多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日志,直到它突然开始记录更新。当它似乎重置所有孩子的状态时,该记录是同步的。

这让我觉得由于某些缓存状态,所有更改都在内存中发生,但我在整个过程中添加了reloadsavefind个调用似乎会改变。我也尝试在uncache中包装这些调用,但这没有用。

1 个答案:

答案 0 :(得分:0)

事实证明,这是因为写入是在长时间运行的事务中保存的,因为状态机gem保持在状态更改和任何after挂钩结束之间打开一个事务。我们编写了在主监视循环上运行了几个小时的钩子。

我们通过在状态更改之间而不是在回调中执行操作来解决此问题。

顺便提一下,错误的行为完全如最新的Red Book所描述的那样,是弱隔离的副作用&#34;在大多数RDMBS中实现并发:

  

示例异常包括读取另一个事务产生的中间数据,读取中止的数据,在执行同一事务期间读取同一项的两个或更多不同值,以及“丢失”由于并发写入事务而导致的某些事务影响项目