以下では具体的な実装は出てきません。概要だけの雑なメモです。
Hive のクエリのテスト
- とにかく時間がかかる
- 待ってる間に何をしてたか忘れる
- ミスると時間がどんどん溶ける
- 最初に不要データを削除するステップが抜けてやりなおし、とか
- めんどくさい
- 無理……
- 自分、こういうのに向いてないな……
- 自動テストがないと(テストのコストが高いと):
- 改良案を思いついても修正やリファクタリングをためらってしまう。良くない。
- バグの発見が遅れる
はじまり
- とにかく最初の手順であるデータ投入、ロードがめんどくさく、ボトルネックっぽい雰囲気が漂っていたので、ここだけでもなんとかできれば楽になりそうと思って始めた
- ssh, scp, hdfs, beeline コマンドあたりが使えればどうとでもなるだろう、くらいの目論見で適当に始めた
ロード
| id | name | ... | | --- | ---- | --- | | 1 | foo | ... | | 2 | bar | ... |
みたいなテキストファイル*1を用意して、
- パースして TSV ファイルに変換
- こういうところは Ruby で
ruby to_tsv.rb input_data.txt > input_data.tsv
- tsv ファイルを作業用サーバ*2に scp で転送
- 同様にシェルスクリプトを scp で転送
- スクリプトをキック
最初はこれだけ。
検証対象クエリの実行
どうせサーバに送ったスクリプトをキックするのなら、 検証対象のクエリの実行までやってしまえばいいじゃない、となった。
- HQLファイルも scp で転送
- ロードの後に beeline で実行
実行結果の検証
- どうせならクエリの実行結果の検証もやりたい……けど、これはちょっと面倒そうなのでしばらく寝かせていた
- しばらく考えていたらなんとかなりそうな気がしてきて着手
- 期待データと実際の結果を両方とも列のアラインメントを揃えてテキストファイルに吐いて diff コマンドに丸投げ
- とりあえずは OK か NG かを機械的に判定できるだけでも助かる
- 実際の結果はこれも beeline コマンドでクエリを実行してリモートから取ってくる
最初は beeline を使って取得していたが、beeline の出力は null の扱いが微妙だったりして、MySQL 用に作った これ を Hive 向けにカスタマイズしたもので取得するようにした。 次のようなフォーマットで出力される(参考: もっとお手軽な機械可読テキストテーブルフォーマット)。 各行は JSON としてパースすればよい。
[ "col1" , "col2" , "col3" , "col4" ] [ "1" , "a" , "" , null ] [ " " , "null" , "12.34" , "\\\t\r\n\"\\\t\r\n\"" ] [ "1234" , "全角テキスト" , "" , "2147483648" ]
スクリプト
シェルスクリプトは上位のグルーコードとして使い、 手順が分かる感じに。
# 不要データ・パーティションの削除など clean # ロード load_table_foo load_table_bar # クエリを実行 execute # 結果の検証 verify
シェルスクリプトだと面倒なもの(データの加工、フォーマット変換など)は Ruby のユーティリティコマンドにする。
使いまわせそうな部分は自分用のユーティリティとして切り出すなど。
https://github.com/sonota88/table_tools
たぶん自分しか使わないだろうし gem にはしていません。
Docker の利用
上でさらっと「scp で転送して〜」などと書いているが、パスの取り回しなどが煩雑でなんとかしたかった。
- そうこうしているうちに Docker が流行ってきた
- Hadoop の設定ファイル、hdfs, beeline コマンドなどが入ったイメージを用意して、 必要なファイルを置いたディレクトリをマウントすることで解決
docker run ...
でスクリプトをキック- scp とパスの管理が不要になってすっきりした
- Docker べんり
テキストデータでの編集のつらみ
テキストファイルは良いものだが……
- カラムが多いテーブル、内容が長いカラム(URLとか)を持つテーブルだとテキストファイル+テキストエディタではつらい……
- 適宜説明コメントとか書きたい。書かないとわからない。
- まとまりごとに色を付けたり、着目しているデータに色を付けたりして見やすくしたい
そこで、スプレッドシートを使うことにした。
- LibreOffice Calc の fods 形式で保存してリポジトリに含める
- fods ファイルを読んで TSV なり JSON Lines 形式なりに変換
ruby extract_sheet.rb input_data.fods sheet_name > input_data.txt
みたいな感じで
- 後はこれまでと同じ
スプレッドシートの書き方は昔考えたこれを使いまわし: JavaScript(jrunscript/Rhino)でodsファイルからMySQLにデータ投入
長いカラム名、struct
foo_bar__baz_baz__hogehoge__piyopiyo__...
みたいな長いカラム名があり、スプレッドシートで扱いづらい- struct なカラムも似た感じでスクリプトでよしなにやるようにした。
- 短いものなら JSON を直接セルに書いたり
無視したいセル
これは Hive に特異的な話ではないですが。
- 検証時の比較で全カラムを比較対象にしてしまうと 最初にテストデータを作るときや修正時に邪魔
(ANY)
と書いたセルは比較の際に無視するようにした- これがないと無理して同じデータに複数の観点を盛り込んだりしがち
- 直行してるのに1レコードにまとめてしまったりとか
- 意図を読み解くのが難しくなる
- なので、これも作ってよかった
この段階で、検証処理は
- 列数・列名が一致しているかチェック
- 件数が一致しているかチェック
- そこまで一致していたら実際の結果の中の
(ANY)
に対応する部分を置換 - 全体を diff で比較
という流れになった。
今に至る
- 上記のようにその都度改良を加えてきて、ここまで 3年くらい
- TDD の手法がそのまま援用できてよい
- 先にテストを書いてクエリ書く
- 先にバグが再現されるテストを書いて修正
- まず赤を緑にしてから青にしていく
- 細かい不満はまだいろいろあるが、かなりましになった
- ただし、 Hive が遅いのは解消されない
- 夜間にまとめて実行したりしている
- とはいえ、精神的にはかなり楽になった。本来やりたいことに集中できる。
TODO
- 並列実行できるようにする
- Hadoop/Hive もコンテナで動かして disposable にできるとよさそう
- Apache Bigtop などの検討
- Hadoop/Hive もコンテナで動かして disposable にできるとよさそう
- Hive を速くする
- 一度 PostgeSQL の併用も試したが、やっぱりちょっと辛い
- まず PostgreSQL を使ってテストケース・テストデータを素早く作り、その後 Hive に切り替えたりしてみた
- DDL・クエリの読み替えがちょっとでは済まない
- 一度 PostgeSQL の併用も試したが、やっぱりちょっと辛い
- スプレッドシート(fods ファイル)の diff を気軽に見れるようにする