需要一个简单,干燥,通用的检查点机制

时间:2012-05-02 00:45:04

标签: ruby ruby-on-rails-3 checkpoint

上下文:我正在进行的许多操作都需要冗长的Web访问。有时Web访问失败,需要重新启动该过程。从头开始重新开始这个过程很痛苦。

因此,我编写了许多特殊方法来检查点:当您重新启动流程时,它会查看检查点数据是否可用并从中重新初始化状态,否则会创建新状态。在操作过程中,该过程会定期将检查点数据写入某个位置(到文件或数据库)。当它完成后,它会清理检查点数据。

我想要一个简单,干燥,通用的检查点机制。你会怎么写的?或者是否有一个模块已经这样做了? (虽然这还不是一个问题,为线程安全实现授予额外的明星!)

2 个答案:

答案 0 :(得分:0)

您实际描述的是state machine。 Web服务是无状态的,但是当您在服务器端提供某些内容的更新时,状态会更新,充当“检查点”,因此可以在事务或“Web访问”之间保持,因为您调用它们

如果你之前从未做过状态机,那可能是一个学习曲线,但你可以查看this page提供“检查指向”或“状态”宝石的列表。 AASM可能会有效,并且正在积极开发中,但根据您需要多少功能,您可以查看屏幕右侧的备选列表,看看哪种方式最适合您。

我知道aasm的一种生产用途是通过多步骤过程自动保存一个人的进度,允许他们放下,断开连接,或者稍后再回来完成它。该过程中的步骤必须以某种顺序完成,并且在大多数情况下存在定义的“完成”状态。 AASM应该能够为你处理这些事情。

答案 1 :(得分:0)

在仔细研究之后,我决定我愿意将这个特定于ActiveRecord。通过利用ruby的ensure工具以及ActiveRecord中的destroyed?changed?方法,设计变得简单:

使用:name和:state

定义Checkpoint模型
# file db/migrate/xyzzy_create_checkpoints.rb
class CreateCheckpoints < ActiveRecord::Migration
  def change
    create_table :checkpoints do |t|
      t.string :name
      t.string :state
    end
    add_index :checkpoints, :name, :unique => true
  end 
end

# file app/models/checkpoint.rb
class Checkpoint < ActiveRecord::Base
  serialize :state
end

定义WithCheckpoint模块

# file lib/with_checkpoint.rb
module WithCheckpoint

  def with_checkpoint(name, initial_state, &body)
    r = Checkpoint.where(:name => name)
    # fetch existing or create fresh checkpoint
    checkpoint = r.exists? ? r.first : r.new(:state => initial_state)
    begin
      yield(checkpoint)
    ensure
      # upon leaving the body, save the checkpoint iff needed
      checkpoint.save if (!(checkpoint.destroyed?) && checkpoint.changed?)
    end
  end
end

样本用法

这是一个有点人为的例子,在经过一些迭代后随机爆炸。更常见的情况可能是冗长的网络或文件访问,在任何时候都可能失败。注意:我们将状态存储在数组中只是为了表明'state'不必是一个简单的整数。

class TestCheck
  extend WithCheckpoint

  def self.do_it
    with_checkpoint(:fred, [0]) {|ckp|
      puts("intial state = #{ckp.state}")
      while (ckp.state[0] < 200) do
        raise RuntimeError if rand > 0.99
        ckp.state = [ckp.state[0]+1]
      end
      puts("completed normally, deleting checkpoint")
      ckp.delete
    }
  end

end

当您运行TestCheck.do_it时,它可能会在经过一些迭代后随机爆炸。但是你可以重新启动它,直到它正确完成:

>> TestCheck.do_it
intial state = [0]
RuntimeError: RuntimeError
        from sketches/checkpoint.rb:40:in `block in do_it'
        from sketches/checkpoint.rb:22:in `with_checkpoint'
        ...
>> TestCheck.do_it
intial state = [122]
completed normally, deleting checkpoint
=> #<Checkpoint id: 3, name: "fred", state: [200]>