【问题标题】:How can I ensure that ActiveRecord is saving changes to the database如何确保 ActiveRecord 将更改保存到数据库
【发布时间】:2014-07-08 02:44:20
【问题描述】:

我有一个复杂的系统,其中涉及许多 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_succeededwork_failed,但 ParentMonitors 仍处于 work_needed 状态.通过跟踪和测试,我确定每次调用ParentMonitor#child_transition 时,处于“需要工作”状态的孩子列表会连续减少,直到某个时候它会加载数据库并将所有孩子的值替换为“需要工作”。尽管有些之前已经完成。

此外,在它突然开始记录更新之前,我在这些最初的几个孩子的日志文件中看不到任何 UPDATE 日志。该日志记录与它似乎重置其所有子项的状态同时进行。

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

【问题讨论】:

  • 将持久性包装在 ActiveRecord.transaction 中,这将确保任何失败都会导致所有事务回滚,并且您将始终处于可预测的状态
  • 我认为您可能想要获取互斥锁并在更经典的多线程场景中执行一些此类操作,以检查两个不同的作业是否相互干扰,这很简单,但效果很好。
  • @bjhaid 我知道 ActiveRecord 已经在 added transactions 周围像 #save 这样的东西。我将尝试将其包装在另一个事务中,以防提交它强制数据库写入。
  • @MikeH-R 感谢您的建议,我将更多地研究互斥体模式,但是 postgresql 是否通过锁定记录来处理来自多个进程的干扰?你能详细说明我将如何应用互斥锁吗?
  • @jwadsa​​ck 引用您发布的链接As a rule, transactions are only needed when changes to multiple records must succeed as a single unit

标签: ruby-on-rails ruby activerecord resque


【解决方案1】:

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

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

顺便说一句,错误行为与最新的Red Book 中描述的完全一样,是大多数 RDMBS 中实现的“弱隔离”并发的副作用:

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

【讨论】:

    猜你喜欢
    • 2015-08-24
    • 2012-07-07
    • 2016-04-09
    • 1970-01-01
    • 2017-10-17
    • 1970-01-01
    • 2017-08-21
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多