0

背景:我正在做的许多操作都需要长时间的网络访问。有时 Web 访问失败,需要重新启动该过程。从头开始重新启动过程很痛苦。

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

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

4

2 回答 2

0

你本质上描述的是一个状态机。Web 服务是无状态的,但是当您在服务器端提供更新时,状态会更新,充当“检查点”,因此可以在事务或“Web 访问”之间持久保存,如您所说.

如果您以前从未使用过状态机,这可能有点学习曲线,但您可以查看此页面,提供“检查点”或“状态”宝石列表。AASM可能会起作用,并且正在积极开发中,但根据您需要多少功能,您可能会查看屏幕右侧的替代列表,以了解最适合您的选项。

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

于 2012-05-02T01:02:39.300 回答
0

在仔细考虑之后,我决定我愿意将它专门用于 ActiveRecord。通过利用 ruby​​ 的ensure功能以及 ActiveRecord 中的destroyed?changed?方法,设计变得简单:

使用 :name 和 :state 定义检查点模型

# 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

样品使用

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

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]>
于 2012-05-05T05:06:59.917 回答