Prestoソースコードリーディング

7
Sadayuki Furuhashi @frsyuki

RecordSetというインタフェースも発見した。RecordCursorを返すメソッドと、List<ColumnType>を返すメソッドを持つ。RecoreSet == テーブルのインタフェースかな。シンプル。

2013-11-15 22:01:04
Sadayuki Furuhashi @frsyuki

ConnectorMetadata, ConnectorSplitManager, ConnectorRecordSetProvider, ConnectorHandleResolver の4つをインタフェースを実装してやれば、カスタムconnectorを実装できる。

2013-11-15 22:05:25
Sadayuki Furuhashi @frsyuki

connectorはデータソースで、メタデータとデータを管理する。データの管理はSplitとRecordReaderに別れ、ファイル群がSplitで管理され、SplitをRecordReaderで読み出す。SplitはPartitionでグループ化されて管理されている。

2013-11-15 22:12:15
Sadayuki Furuhashi @frsyuki

Splitは、そのファイルが保存されているホストのアドレスを持つ任意のオブジェクト。HDFSを意識した設計だけど、ちゃんと疎結合化されているから、ハックするのは簡単そう。

2013-11-15 22:14:33
Sadayuki Furuhashi @frsyuki

カラムの属性に、パーティションキーであるか否かというフラグがあるな。一方でPartitionにはそのキーを返すメソッドがある。パーティションキーになっているカラムに対してWHERE文がかかっていたら、それに対応するPartitionをとってくるのかな。

2013-11-15 22:19:01
Sadayuki Furuhashi @frsyuki

さらにそのPartitionの中のSplit群に対してRecordCursorを作って、データを読み出す。パーティショニングはハッシュパーティショニングが前提なのかなぁ。パーティションキーになっているカラムに対し、WHEREで不等式を使って範囲を指定した場合の挙動が気になる。

2013-11-15 22:21:00
Sadayuki Furuhashi @frsyuki

パーティションなる粒度にして欲しく無いんだけども、ここは困ったな。100 < partitionKey AND partitionKey < 500 とか書いたら、100から500まで400個ものパーティションIDを検索するのかな。範囲が86400とかになったらどうなる…

2013-11-15 22:25:22
Sadayuki Furuhashi @frsyuki

む。ハック可能になっているな。ConectorSplitManager.getPartitionsメソッドに対して、定数が右辺値になった条件が渡るようになっている。そうして一旦絞り込んで得られたPartitionに対してさらにパーティショニングキーでマッチをかけるという二段構え。

2013-11-15 22:31:53
Sadayuki Furuhashi @frsyuki

しかも丁寧にも全カラムに対する定数条件が渡ってくるな。これはpushdownが実装しやすい。前段でのみ絞り込み、後段のPartitionに対するパーティショニングキーを使ったマッチは常にtrueになるように細工すれば良さそう。

2013-11-15 22:33:08
Sadayuki Furuhashi @frsyuki

ぁぁ…いやダメだコレ。定数が右辺値になった条件と言っても、オペレータが常に == だ。それは困る。< とか > も拾って欲しいのだけど、ExpressionUtil.extractConstantValuesと、プラグインインタフェース自体も変更しないとダメ。

2013-11-15 22:34:21
Sadayuki Furuhashi @frsyuki

ConnectorSplitManagerWithRange extend ConnectorSplitManagerみたいなインタフェースを追加したり、ExpressionUtil.extractConstantRangesを実装したりが必要だな。

2013-11-15 22:39:07
Sadayuki Furuhashi @frsyuki

それにSplitManagerを変更して instanceof ConnectorSplitManagerWithRange で分岐して処理を切り分ける実装が必要になるな。

2013-11-15 22:39:44
Sadayuki Furuhashi @frsyuki

そしてExpressionUtil.extractConstantRangesの実装は結構複雑になるな。右辺値はオペレータによっては必ずしも定数でなくても良い。例えば column1 < 条件の右辺値は、定数でなくても取り得る最大の値が分かれば絞り込みが可能になる。

2013-11-15 22:41:00
Sadayuki Furuhashi @frsyuki

このあたりは真面目に実装してpull-reqする手はあるな。右辺値はUDFでも良いから、UDFがプラグインで追加可能な仕様であるならば、extractConstantRangesも拡張可能である必要があり、少々複雑になり得るけども。

2013-11-15 22:42:34
Sadayuki Furuhashi @frsyuki

UDFにgetPossibleLargestValue()とgetPossibleSmallestValue()メソッドが付いていると、その辺りの実装はしやすくなるけど、それはそれで難しそうだな。

2013-11-15 22:43:23
Sadayuki Furuhashi @frsyuki

そう言えばUDFのインタフェース定義を見ていない。プラグイン機構はあるのかな。

2013-11-15 22:43:40
Sadayuki Furuhashi @frsyuki

LocalExecutionPlannerという実行計画ノードを発見した。ローカル実行モードがありそうだ。

2013-11-15 22:46:26
Sadayuki Furuhashi @frsyuki

FunctionRegistryというクラスがある。シンプルなscalar関数、aggregate関数、window関数が登録されている。このListはimmutableだなぁ。関数の追加は意図していないのだろうか。でも簡単な変更にプラガブルにすることができそう。

2013-11-15 22:49:14
Sadayuki Furuhashi @frsyuki

UDTFに相当するインタフェースはないのかな。LATERAL VIEWは無いか。

2013-11-15 22:49:47
Sadayuki Furuhashi @frsyuki

いっそリフレクションでぶっ込めば、現状のコードでも追加はできるし、それほど難しく無さそう。Hiveと違い、GenericUDFは存在せず、各型についてメソッドを定義しないといけない。正しい選択だと思う。

2013-11-15 22:51:26
Sadayuki Furuhashi @frsyuki

1つのscalar関数は1クラスではなく1メソッド。これなら実装はもの凄く簡単だな。しかし同時に決定性な関数しかかけなくなっているあたり賢いけど、勢い余って汚い関数を書きたくなったときに困りそう。まぁwindow関数があるから必要ないかな。

2013-11-15 22:53:12
Sadayuki Furuhashi @frsyuki

集約関数は、固定長引数と可変長引数で二つの基底クラスがある。いずれもデータの入力、中間集約結果の出力、中間集約結果の入力、最終集約結果の出力、この4つのメソッドがメイン。謎なmodeとか無くて非常に分かりやすい。

2013-11-15 22:57:19
Sadayuki Furuhashi @frsyuki

Hiveを見た後だと、このインタフェース設計は良くできていると思った。

2013-11-15 22:57:53
Sadayuki Furuhashi @frsyuki

ソースは読みやすくて、疎結合化がしっかりしていてメンテナンスしやすい印象。あとは性能が出るのであれば、色々なコネクタが出てきて長く生き残る製品になるのではないかな。

2013-11-15 23:04:01
Sadayuki Furuhashi @frsyuki

例えば先のツイートのようにPartition一覧の絞り込み条件に範囲を受け取れるように改造を施せば、バックエンドにRDBMSを使用できる。具体的には分散したPostgreSQLにデータを保存し、PrestoでMPPを投げる、StadoのようなDWH製品はシンプルに作れるはず。

2013-11-15 23:06:20