(2012-04-01) オレオレ訳。後半は力尽きてかなりいい加減。
ストレージの実装
Ruote は、メモリ上とファイルシステム上の二種類のストレージを持つ。その他のストレージも利用可能であるが、それぞれ長所、短所がある。
格納するオブジェクト(式、ワークアイテム、エラー、スケジュール、...)に応じて異なるストレージを選択する複合型のストレージもある。
独自のストレージを実装する必要がある場合もある。このページは助けになるべきである。(メーリングリスト上で叫んでいない場合)
ストレージの実装は、ストレージ インターフェースに従う必要がある。
少し歴史を
ストレージは、ruote 2.1.x の核心である。
engine = Ruote::Engine.new(Ruote::Worker.new(ThatStorage.new(info)))
# or
engine = Ruote::Engine.new(ThatStorage.new(info))
ワーカーとエンジンはストレージが必要とする。それらは、共有ストレージを介して連携する。
Ruote は永続性を必要とする。プロセスは、エンジンよりも寿命が長い場合がある。エンジンがダウンした場合、プロセスはストレージに格納される。ワーカーが復帰すると、プロセスは再開されなければならない。
Ruote 2.1.x の複数ワーカーは、ワーカーが互いに上書きや削除するのを防止する必要性をもたらした。
ドキュメント
Ruote 2.1.x のストレージは、CouchDB に強く影響を受けている。特に、ドキュメントとバージョンの概念に影響を受けている。
Ruote は全てのデータをドキュメントとして表現する。ドキュメントは JSON シリアライズ可能なハッシュである。“_id” 、“type” と “_rev” を項目として持つ。
‘type’ は、“expressions” 、“errors” 、“workitems” 、“schedules” のような文字列である。
‘_id’ は、ドキュメント識別子である。
ドキュメント タイプ
ドキュメントには、7+1 種類のタイプがある。
タイプ | 説明 |
---|---|
expressions | プロセス インスタンスの部品 |
msgs | expressions からの/への ‘messages’ |
schedules | 後に処理するためにスケジュールされた msgs |
errors | プロセス実行中に発生した errors |
variables | エンジン グローバルな変数 |
configurations | エンジンのコンフィグレーション |
workitems | StorageParticipant ワークアイテム |
history | (Ruote::StorageHistoryを使っている場合) |
一貫性
Ruote は複数のドキュメントを確保する。ストレージの実装が排他的更新を提供できない場合、単一のワーカーに制限される。
ワーカーの詳細については コンフィグレーション ページ を参照のこと。
インターフェース
#--
# 主なメソッド
#++
# ドキュメントの格納
#
# opts:
# ... TODO ...
#
# 戻り値:
# * true ドキュメントが削除されていた場合
# * a document バージョン(rev)が変更された場合
# * nil 保存が成功した場合
#
def put(doc, opts={})
end
# ドキュメント タイプとキー(_id)によるドキュメントの取得
#
def get(type, key)
end
# ドキュメントの削除
#
# 戻り値:
# * true 既に削除済みの場合
# * a document 与えられたドキュメントのバージョン(rev)が現在のバージョンと異なる場合
# document
# * nil 削除が成功した場合
#
def delete(doc)
end
# 特定のタイプの複数のドキュメントを取得する
#
# 入力:
# - key が指定されない場合、全てのドキュメントを返す
# - Array(key) にドキュメントの _id がマッチするものを返す
#
# opts:
# [:descending] _id で降順でソートする
# [:count] 件数を返す
# [:skip] X 件スキップする
# [:limit] X 件に制限する
#
# 戻り値:
# * An Integer (:count が指定された場合)
# * An array of documents
#
def get_many(type, key=nil, opts={})
end
# 指定されたドキュメント タイプの _id のリストを返す
#
def ids(type)
end
#--
# 補助的なメソッド
#++
# 全ての msgs 、schedules 、errors 、expressions と workitems を削除する
#
# It's used mostly when testing workflows, usually when cleaning the
# engine/storage before a workflow run.
#
def clear
end
# ストアをクリアする
#
def purge!
end
# 新しいドキュメント タイプを追加する。いくつかのストレージでは必要
#
def add_type(type)
end
# 指定されたタイプのドキュメントを削除する
#
def purge_type!(type)
end
モデルとしての実装
実装の例。 hash_storage.rb
テスト
ストレージの実装は、ユニットテストと機能テストを実施されるべきだ。複数ワーカーで利用される場合、同時テストも実施すべきだ。
テストは、ruote/ ディレクトリから実行される。“redis” や “sequel” といった実装名を持つ拡張子を渡すと、テストは自動的に行われる。
このセクションの残りでは、“ruote-pasta” という実装名で、ruote-pasta/ ディレクトリに格納したと仮定する。
ストレージ実装は、“new_storage” メソッドを含む test/functional_connection.rb を提供する必要がある。これは、テストのためにストレージをセットアップするための ruote テストスイートを含む。ruote-redis や ruote-sequel の functional_connection.rb を参照のこと。
ユニット テスト
ruote のユニットテストにおいて、ストレージのテスト関連はこれだけである。
$ ruby test/unit/storage.rb -- --pasta
このテストをグリーンにすることが、ストレージ開発の最初の目標だ。
機能テスト
ストレージ実装は全ての機能テストを通過すべきだ。
$ ruby test/functional/test.rb -- --pasta
いくつかのダングリングテストに困難がある場合、メーリングリスト を参照のこと。
同時テスト(複数のワーカー)
複数のワーカーが稼働しているように ruote ストレージをテストする 3つのテストがある。それらは test/functional/ の下にあり、“ct_” が先頭についている。
ct_ テストを200回実行しても失敗しなければ、複数ワーカーで問題ないといえる。
$ ./test/looper.sh test/functional/ct_0_concurrence.rb -- --pasta 200
$ ./test/looper.sh test/functional/ct_1_iterator.rb -- --pasta 200
$ ./test/looper.sh test/functional/ct_2_cancel.rb -- --pasta 200
これらのテストはワーカーの間で衝突を起こす。