オレオレワークフローエンジン作りたくなる病というのがあって、例えば make 系なんかだと Wikipediaに色々書いてあったりします。ワークフローエンジンと総称するのが正しいのか知りませんが、依存グラフを順ぐりに並列に実行していくようなもの、色々あるわけですけど、目的も粒度もバラバラですが以下のようなものは割と似たようなものに見えてます。
- thread pool
- Tensorflow
- make
- Digdag
閑話休題。なんか微妙に10秒程度待ち時間があるような処理があって(クラウドストレージに対する例えば ls とか)、その ls の結果全てに対してまた10秒程度時間がかかるコマンドをまとめて実行、終わったら結果を集計、みたいなことをしたりすることが最近多いです。1週間から1ヶ月ほど10回とか100回使うんだけど、でもそのうち捨てる系のコード。私の場合、よくやる解決と問題点は
- shell script で並列化したいところはバックグラウンド実行+waitで散らして、最後の部分は ruby かなんかで書く: shell script と ruby の2つを管理するのがうっとうしい。多少ややこしいパイプラインを作るの大変
- ruby かなんかでスレッドプール作ってやる: 分散部分のコードがめんどくさい。多少ややこしいパイプラインを作るのがかなりめんどくさい
- make を使う: 動作は理想的なことが多いけど、コードがめんどくさくて、あと宣言的な記述になって、シーケンシャルな記述じゃなくなるので、読みにくく編集しにくい
欲しいことを考えると
- CPU並列は無くても良い
- パフォーマンスは最適でなくて良い
- コマンドが手軽に実行できると良い
- コマンドが失敗したら即座に止まると良い
- shell はミスりやすいから嫌だ
- 処理の後半部分のやりなおしとかのためにチェックポイントを仕込めると良い
で、まあなんか作りはじめてみましたというのがこれ。
https://github.com/shinh/drdr/
あまり良い例が思いつかなかったのですが、自分のスライドのディレクトリにあるスライド全部に対して、それぞれページ数を集計するサンプルがこんな感じ。
URL = "http://shinh.skr.jp/slide/" drdr { # スライド一覧のところを読んできて、 list.ckpt に保存しておく # これ以降のスクリプトに書き損じがあっても、再度ネットワークアクセスは発生しない cmd("curl #{URL}", ckpt: 'list.ckpt') | task{|l| # ここから list.ckpt に依存した処理で、 Apache の directory index を適当にパース l.scan(/href="([a-z].*?)\/"/).map do |m| name = m[0] # スライドのページ数を適当に取ってくる。このコマンド実行は並列に走る cmd("curl #{URL}#{name}/ 2> /dev/null") | task{|l| last_page = 0 l.scan(/(\d+)\.html/) do last_page = [$1.to_i, last_page].max end [name, last_page] } end } | task{|a| # 全部終わったら CSV として出力 a.each do |name, max_page| puts [name, max_page] * "," end } }
などなど。妄想したほど使い勝手良くなってない気がするのですが、しかし自分が使える程度のものにはなってるような気がします。しかし一般的にこういうのはどういう感じで処理してるもんなんでしょうか。なんかいい方法あったら教えてください。。
あと Guild が実装されると計算も並列に走るようになっていい気がしますね