HiveQL テスト 自動化 メモ

以下では具体的な実装は出てきません。概要だけの雑なメモです。


Hive のクエリのテスト

  • とにかく時間がかかる
    • 待ってる間に何をしてたか忘れる
    • ミスると時間がどんどん溶ける
      • 最初に不要データを削除するステップが抜けてやりなおし、とか
  • めんどくさい
    • 無理……
  • 自動テストがないと(テストのコストが高いと):
    • 改良案を思いついても修正やリファクタリングをためらってしまう。良くない。
    • バグの発見が遅れる

はじまり

  • とにかく最初の手順であるデータ投入、ロードがめんどくさく、ボトルネックっぽい雰囲気が漂っていたので、ここだけでもなんとかできれば楽になりそうと思って始めた
  • ssh, scp, hdfs, beeline コマンドあたりが使えればどうとでもなるだろう、くらいの目論見で適当に始めた
    • 想定外の柔軟な対応が必要になってもこれらが直に使えればなんとかなるだろう
    • こういうべんりツール作るための工数は降ってこないので(降ってきてほしいですが)、片手間でなんとかできる範囲でババッとなんとかする
    • とりあえず素直に Unix 哲学に従っておけばなんとかなるのでは

ロード

| id  | name | ... |
| --- | ---- | --- |
| 1   | foo  | ... |
| 2   | bar  | ... |

みたいなテキストファイル*1を用意して、

  • パースして TSV ファイルに変換
    • こういうところは Ruby
    • ruby to_tsv.rb input_data.txt > input_data.tsv
  • tsv ファイルを作業用サーバ*2に scp で転送
  • 同様にシェルスクリプトを scp で転送
  • スクリプトをキック
    • 一時テーブルを drop/create table
    • データの削除+ロード (external table にしておいて hdfs dfs -put)
    • 一時テーブルから目的のテーブルに select insert

最初はこれだけ。

検証対象クエリの実行

どうせサーバに送ったスクリプトをキックするのなら、 検証対象のクエリの実行までやってしまえばいいじゃない、となった。

  • 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にデータ投入

f:id:sonota88:20200530095015p:plain

長いカラム名、struct

f:id:sonota88:20200530095924p:plain

無視したいセル

これは Hive に特異的な話ではないですが。

  • 検証時の比較で全カラムを比較対象にしてしまうと 最初にテストデータを作るときや修正時に邪魔
  • (ANY) と書いたセルは比較の際に無視するようにした
  • これがないと無理して同じデータに複数の観点を盛り込んだりしがち
    • 直行してるのに1レコードにまとめてしまったりとか
    • 意図を読み解くのが難しくなる
    • なので、これも作ってよかった

f:id:sonota88:20200530100430p:plain

この段階で、検証処理は

  1. 列数・列名が一致しているかチェック
  2. 件数が一致しているかチェック
  3. そこまで一致していたら実際の結果の中の (ANY) に対応する部分を置換
  4. 全体を diff で比較

という流れになった。

今に至る

  • 上記のようにその都度改良を加えてきて、ここまで 3年くらい
  • TDD の手法がそのまま援用できてよい
    • 先にテストを書いてクエリ書く
    • 先にバグが再現されるテストを書いて修正
    • まず赤を緑にしてから青にしていく
  • 細かい不満はまだいろいろあるが、かなりましになった
  • ただし、 Hive が遅いのは解消されない
    • 夜間にまとめて実行したりしている
    • とはいえ、精神的にはかなり楽になった。本来やりたいことに集中できる。

TODO

  • 並列実行できるようにする
    • Hadoop/Hive もコンテナで動かして disposable にできるとよさそう
  • Hive を速くする
    • 一度 PostgeSQL の併用も試したが、やっぱりちょっと辛い
      • まず PostgreSQL を使ってテストケース・テストデータを素早く作り、その後 Hive に切り替えたりしてみた
      • DDL・クエリの読み替えがちょっとでは済まない
  • スプレッドシート(fods ファイル)の diff を気軽に見れるようにする

参考

*1:https://memo88.hatenablog.com/entry/2019/04/08/051445

*2:開発環境の Hadoop クラスタ用の設定と hdfs, beeline コマンドなどが用意してあるサーバ