- イベント名:Treasure Data Tech Talk 201607
- 開催日時:2016-07-14(木)
- 会場:イベント&コミュニティスペース dots.
先週末に、Treasure Data Tech Talk に参加してきました。このイベントは毎回濃い話を聞けるので、行けるときはなるべく参加するようにしています。
今回は、古橋さんによる Digdag での YAML 利用の話と、成瀬さんによる PerfectQueue の話が特に面白かったです。以下、講演内容のメモと、公開済みのスライドです。
講演内容
DigdagはなぜYAMLなのか? (Sadayuki Furuhashi, @frsyuki)
Digdag とは何か?
- Workflow automation system
- Digdag で一番やりたいのはバッチデータ解析の自動化
Digdag の競合
- OSS, Proprietary それぞれに競合がある
- Workflow automation system は、ワークフローの定義方法によって3つに分類できる
- プログラミング言語型:Luigi など
- GUI型:Rundeck など
- 定義ファイル+スクリプト型:Azkaban など
- ワークフローの作りやすさと、カスタマイズの柔軟性のトレードオフ
Digdag
- Digdag は定義ファイル+スクリプト型
- 定義ファイル+スクリプト+俺たちのYAML
- YAMLは便利だが、include できない、変数の埋め込みができない、(言語内DSLのように)プログラムが書けない、という欠点がある
- Digdag では、YAML の仕様に従ったうえで、これらの欠点を克服した
include できる
- YAML の仕様では、値(scalar)の前に "!" から始まる文字(タグと呼ばれる)を付与できる
- 通常、YAMLパーサは、正規表現によるマッチでタグを決定して、自動的にタグを付与している
- Digdag では "!include : filename" という表記を、ファイルインクルードの文法として使っている
- !include の後ろに、" " を書く必要がある。このスペースが大事。このスペースのおかげで、通常の YAML パーサでも、キーが " "、値が filename のハッシュとして読み込める
- ただし、この !include を複数書くと、キーが " " のハッシュが重複してしまう。Digdag の YAML パーサでは、複数の !include を書けるように、内部的にキーを UUID に書き換えている
変数の埋め込みができる、プログラムが書ける
- Java 8 は、Nashorn(ナスホーン)という JavaScript Engine を同梱している。これを使って ${} 内を評価している
- だから Digdag は Java8 必須
Q) 何故YAMLをベースにした?
- A) 比較的書きやすい、読みやすい。YAMLとして既存のプログラムから扱える。
Q) YAMLはグラフ構造を表現するのに適さないのでは?
- A) YAMLはDAG、グラフを扱うわけではない。ツリーを扱っている。
Q) YAMLにコードを書けるとのことだが、悪さはできないのか?
- A) 悪さできないように対策している。JavaScript はサンドボックス内でしか動作しない。そのためにJavaScriptを採用した。
PerfectQueueはいかにパーフェクトか、あるいはRubyとMySQLでジョブキューを作る試みについて (Yui Naruse, @nalsh)
Who is naruse
- nkfメンテナ
- Rubyコミッタ
- Treasure DataではバックエンドのRubyを担当
そもそもジョブキューとは
- FIFO
- フロントエンドとバックエンドを疎結合化
PerfectQueue の特徴
- MySQL で実装
- At-least-once を優先(at-least-once と at-most-once はトレードオフの関係)
キューのデータ構造
CREATE TABLE `queue` ( /* unique key (-> at most once) */ id VARCHAR(255) NOT NULL, /* for FIFO's timeline */ timeout INT NOT NULL, /* opaque data */ data LONGBLOB NOT NULL, /* alive or finished */ created_at INT, PRIMARY KEY (id) )
タスクのライフサイクル
- タスクの投入時に、timeout, created_at を現在時刻にする
- タスクの取得時は、timeout が小さいものから優先的に取得し、timeout を 300 秒後に更新
- タスクの実行中は、ハートビートとして、timeout を定期的に 300 秒後に更新
- タスクの完了後は、created_at を NULL にして、一定時間保存するために timeout を 720 秒後に更新(タスクの重複を検出するため)
- 前述の retention time を過ぎたら物理削除
タスクの取得、削除時の排他制御が大変
- 排他処理のために、最初期は FOR UPDATE を使っていた
- しかし、頻繁にデッドロックする
- デッドロックを避けるには MySQL の気持ちになってクエリを書く必要がある
- LOCK TABLES を使うと、テーブル全体をロックしてしまうので、SELECT にも影響
- GET_LOCK が一番安全、でもクエリの書き方によってはデッドロックが発生した
- ロックの delete クエリと acquire のクエリがバッティングしないように、タイミングの調整が必要(間隔を乱数で変える)
- 調整しないと性能問題が起きた
- さらに、ネットワーク遅延が発生すると影響大
- GET_LOCK から RELEASE LOCK まで 3 RTT かかるので、影響大
- queue テーブルに owner カラムを追加したうえで、FOR UPDATE を使うクエリに変更し、テーブルロックを不要にした
- MySQL の気持ちになって書いたので安全
- 排他処理のために、最初期は FOR UPDATE を使っていた
結論
- PerfectQueue はより完璧になった
Q) 何故 MySQL をキューに選んだのか?
- A) 当時、Amazon RDS で PostgreSQL が使えなかった。RDBMS を選んだ理由は、Amazonで提供されている、フェイルオーバーがある、など。新しいジョブキューを開発したのは、ジョブキューにフェアスケジューリングの機能(Treasure Dataのサービスで必要)を付けたかったから。(古橋)
感想
Digdag での YAML の拡張(いや、標準の仕様に従っているので拡張と言うのは不適切か?)については、話としては面白かったんですが、そもそも YAML でプログラミングするのは辛そう、というのが第一の感想でした。Ansible もそんな感じのつらみがありますしね。AnsibleSpec みたいな、Digdag で書いたワークフローをテストするツールとかが、いずれ出てきたりするんでしょうか。
また、マークアップ言語として YAML にこだわる必要があるんだろうか、とも思ったのですが、じゃあ代替手段として何があるのか、と考えてみると、なかなか難しそうです。XML よりもマシな選択肢となると、いまは YAML なのかな……。HashiCorp の HCL のように、独自方式を作る方向もあったと思いますが、Digdag に独自方式を作るほどの要件はなかったんですかね。