hive-modoki: かんたんな Apache Hive のクローンを作っていた話

2021-06-08 追記

Hive を使うお仕事から離れることになったため、 hive-modoki は開発中止となりました。 転職先が見つかるとかで開発終わるパターンは織り込み済で、半分は作る事自体が目的ということにして保険かけてたので、まあ、大丈夫。

パーサやクエリエンジン部分は何か別の所で再利用できるといいですね。

概要

  • Apache Hive のしょぼいクローン

動機

  • Hive テスト 自動化 メモ にいろいろ書いたように工夫はしていたが、それでも限界がある
  • 遅いのをなんとかしたい
    • プロトタイピング、開発中の検証、自動テストだけでも速くできないか?
    • 仕事の時間のほとんどが Hive クエリの実行を待つ時間みたいになっており、仕事の質として非常によろしくない。転職を考えないとまずいレベル。
      • 「この半年何をしていましたか?」「Hive のクエリ書いてましたね……」
    • イテレーションが長過ぎる。イテレーションを速く回したい

方針

  • 用途をプロトタイピング、開発中の検証、自動テストに限定する
    • 完全なクローンである必要はない
    • サイズの大きなデータを扱う必要はない。 小さなデータだけを想定する。
  • ひとまず非公開
    • ひとまず自分用
    • 人に読まれる前提で書かないといけないとなると、やはり追加のコストがばかにならないので……
    • 自分が必要な機能だけを作る
    • 公開については自分で使えるようになってから考える
  • 汚くてよい
    • ある程度機能の揃った RDB を作るのは今回が初めてなので、どうせ大したものは作れない。初回から良いものを作るのは無理。
      • と思うことにしてハードルを下げる
    • 1年後にきれいなものができるより1ヶ月後に動くものが使える方が嬉しい。minimum viable product を目指す。
    • 個人が仕事以外の時間で作るものなので大したリソースは捻出できない。その範囲でなんとかする。
    • 仕事でこんなの書いたらどやされる、という感じの出来上がりになっております
  • UDF をそのまま動かすのは大変そうなので簡易に済ます。ここは諦める。

手間をかけずに済ませたいので、自分が慣れているもの、枯れているものを使う方向で。

  • HiveQLパーサ
    • Ruby + Racc
      • JRuby でもそのまま動く?
    • AST(S式)に変換
  • エンジン
    • Java
    • ASTを受け取って実行
      • 素朴に木の末端から evaluate していく方式で今のところ何とかなっている
  • HDFS

メモ

↓ここらへんを先にやっておいてよかった、というか、やってなかったらそもそも Hive のクローンを作ろうと考えなかったと思います。

TODO

まず各機能の最低限の部分を作り、後から必要な部分を肉付けしていく感じで。

select

  • [v] select ... from ...
  • [v] order by
  • [v] where
  • [v] join
    • [v] left outer, inner
  • expression
    • [v] 関数呼び出し
    • [v] case
  • [v] group by
  • [v] サブクエリ
  • [v] union all
  • [v] CTE(with句)
  • [v] cast
  • [v] from句なしの select
  • builtin functions
    • [v] coalesce
    • [v] array / named_struct
    • [v] date_format / unix_timestamp / from_unixtime
  • window functions
    • [v] row_number
  • [v] UDF
  • [v] macro
  • 演算子
    • [v] between

  • [v] int / tinyint / bigint / smallint
  • [△] decimal
  • [v] string
  • [v] bool
  • [v] timestamp
  • [v] array / struct

その他

  • [v] create table
  • [v] insert
    • [v] insert ... values ...
    • [v] select + insert
    • [v] dynamic patition
  • [v] use
  • [v] show tables / show databases
  • alter table
  • [v] CLI interface / 対話的実行
  • [v] スプレッドシート(fods ファイル)の内容をロード
  • [v] JsonSerDe(ロードのみ)

関連

↓これらを先にやっておいたのがよかった。これらをやっていなかったら、そもそも自分で作れるかもと考えなかったはず。

進捗

2021

3/11 from句なしの select

3/13 macro / timestamp

3/25 対話的実行

4/10 array / struct

4/22 CTE + select + insert


4/24

array, struct の型まわりのハリボテだった部分を実装。 JSON のパースに Gson を使っていて int が double としてパースされていたのが、 型の情報を見て int に変換できるようになった。

select t1.id, t2.id
from t1
  inner join t2
    on t2.id = t1.xs[0].a

のような array/struct の値を使った join もできるようになってる。


4/25

  • 組み込み関数を追加: coalesce
  • use: DB が存在しない場合はエラーにする
  • create table の際にディレクトリも作成
  • Print (#{num_rows} rows) (beeline-modoki)
  • select 'fdsa' as foo;
  • 二項演算子を追加: <>, -

4/26

  • funcall を expr に含める
  • create database if not exists ...
  • insert: カラム数の一致をチェック
  • insert ... values で null を書けるようにした
  • feat: binary op *
  • feat: unary op: -, is not null
  • add jar (haribote)

4/27

  • feat: binary op: <, <=, >, >=
  • feat: builtin functions: array, named_struct
  • feat: dynamic partition (select + insert)

4/28


4/29

  • feat: between

4/30

  • feat: JsonSerDe(ロードのみ)
  • feat: smallint

5/1

  • fodsファイルからデータを抽出
  • → JsonSerDe で一時テーブルにロード
  • → select+insert(+dynamic patition) で目的テーブルに移し替え
    が通して動くようになった。

5/2

  • create文の stored as / location / tblproperties のパース
    • とりあえずパースだけ。まだ使っていない。
  • create temporary function ... のパース
    • 同じくパースだけ
  • クエリの実行に失敗したら AST をファイルにダンプして、 JUnit のテストからそのファイルを読んで実行できるようにしてみた。 失敗したときにすぐに IDE のデバッガで調べられて便利。

5/16

  • tinyint と int を比較するときの暗黙の型変換とか、単項演算子 - がまだだったとか、細かいところをちまちまと実装