Connecting to Hive from Erlang
ErlangでHiveに接続し、クエリを実行させてその結果を取得する、ということができないか試してみました。HiveはThriftのインターフェースを提供しているので、Thrift経由で接続してみたいと思います。
Install Hive Server
疑似分散モードのHadoopをインストールし、Hadoopを起動後、Hiveをインストールします。
$ sudo apt-get install hadoop-0.20-conf-pseudo
$ vi /etc/hadoop-0.20/conf/hadoop-env.sh (JAVA_HOMEを設定)
$ hadoop namenode -format
$ for service in /etc/init.d/hadoop-0.20-*; do $service start; done
$ apt-get install hadoop-hive-server
参考: Ubuntu 12.04 LTSにHadoopの実験環境 (疑似分散モード)を構築 - 森薫の日記
Install Thrift
thriftをインストールします。cpp版が動けば良いのでconfigure時に細かい設定をしていません。
$ cd /usr/local/src
$ wget http://ftp.meisei-u.ac.jp/mirror/apache/dist/thrift/0.9.0/thrift-0.9.0.tar.gz
$ tar xvfz thrift-0.9.0.tar.gz
$ cd thrift-0.9.0/
$ ./configure --prefix=/opt/thrift-0.9.0
$ make
$ make install
Hiveの.thriftファイルは「share/fb303/if/fb303.thrift」をincludeしていますが、このファイルはHiveのソースコード下には無く、thriftのcontribの下に存在しています。さらに「share」というパスが無いので、このままだとうまくincludeできません。contribをshareに変えるsymlinkを作成して調整しておきます。
$ ln -s /usr/local/src/thrift-0.9.0/contrib /usr/local/src/thrift-0.9.0/share
Generate Erlang Code
Hiveの.thriftファイルを指定し、Erlangのコードを生成します。
$ /opt/thrift-0.9.0/bin/thrift -gen erl -I /usr/local/src/hive-0.7.1-cdh3u6/src -I /usr/local/src/thrift-0.9.0 /usr/local/src/hive-0.7.1-cdh3u6/src/service/if/hive_service.thrift
$ /opt/thrift-0.9.0/bin/thrift -gen erl -I /usr/local/src/hive-0.7.1-cdh3u6/src -I /usr/local/src/thrift-0.9.0 /usr/local/src/hive-0.7.1-cdh3u6/src/metastore/if/hive_metastore.thrift
$ /opt/thrift-0.9.0/bin/thrift -gen erl -I /usr/local/src/hive-0.7.1-cdh3u6/src -I /usr/local/src/thrift-0.9.0 /usr/local/src/hive-0.7.1-cdh3u6/src/ql/if/queryplan.thrift
$ /opt/thrift-0.9.0/bin/thrift -gen erl -I /usr/local/src/hive-0.7.1-cdh3u6/src -I /usr/local/src/thrift-0.9.0 /usr/local/src/thrift-0.9.0/share/fb303/if/fb303.thrift
Erlangのコードがgen-erlフォルダの下に生成されます。
$ ls -l gen-erl/
total 144
-rw-rw-r-- 1 masayuki masayuki 1488 Jun 3 08:26 facebookService_thrift.beam
-rw-rw-r-- 1 masayuki masayuki 3045 Jun 3 08:25 facebookService_thrift.erl
-rw-rw-r-- 1 masayuki masayuki 116 Jun 3 08:25 facebookService_thrift.hrl
-rw-rw-r-- 1 masayuki masayuki 153 Jun 3 08:25 fb303_constants.hrl
-rw-rw-r-- 1 masayuki masayuki 656 Jun 3 08:26 fb303_types.beam
-rw-rw-r-- 1 masayuki masayuki 325 Jun 3 08:25 fb303_types.erl
-rw-rw-r-- 1 masayuki masayuki 299 Jun 3 08:25 fb303_types.hrl
-rw-rw-r-- 1 masayuki masayuki 1126 Jun 3 08:24 hive_metastore_constants.hrl
-rw-rw-r-- 1 masayuki masayuki 3572 Jun 3 08:26 hive_metastore_types.beam
-rw-rw-r-- 1 masayuki masayuki 12096 Jun 3 08:24 hive_metastore_types.erl
-rw-rw-r-- 1 masayuki masayuki 6046 Jun 3 08:24 hive_metastore_types.hrl
-rw-rw-r-- 1 masayuki masayuki 160 Jun 2 04:15 hive_service_constants.hrl
-rw-rw-r-- 1 masayuki masayuki 980 Jun 3 08:26 hive_service_types.beam
-rw-rw-r-- 1 masayuki masayuki 1210 Jun 2 04:15 hive_service_types.erl
-rw-rw-r-- 1 masayuki masayuki 864 Jun 2 04:15 hive_service_types.hrl
-rw-rw-r-- 1 masayuki masayuki 157 Jun 3 08:24 queryplan_constants.hrl
-rw-rw-r-- 1 masayuki masayuki 1928 Jun 3 08:26 queryplan_types.beam
-rw-rw-r-- 1 masayuki masayuki 4559 Jun 3 08:24 queryplan_types.erl
-rw-rw-r-- 1 masayuki masayuki 3331 Jun 3 08:24 queryplan_types.hrl
-rw-rw-r-- 1 masayuki masayuki 4616 Jun 3 08:26 thriftHiveMetastore_thrift.beam
-rw-rw-r-- 1 masayuki masayuki 24794 Jun 3 08:24 thriftHiveMetastore_thrift.erl
-rw-rw-r-- 1 masayuki masayuki 184 Jun 3 08:24 thriftHiveMetastore_thrift.hrl
-rw-rw-r-- 1 masayuki masayuki 1396 Jun 3 08:26 thriftHive_thrift.beam
-rw-rw-r-- 1 masayuki masayuki 2848 Jun 2 04:15 thriftHive_thrift.erl
-rw-rw-r-- 1 masayuki masayuki 168 Jun 2 04:15 thriftHive_thrift.hrl
Setup Project
生成したコードを使ってHiveに接続するモジュールを作成します。まだほとんどErlangでコードを書いたことがない上、rebarを使ってアプリケーションの下地を作るのは初めてなので、見よう見まねです。
$ rebar create-app appid=thrift-hive
==> thrift-hive (create-app)
Writing src/thrift-hive.app.src
Writing src/thrift-hive_app.erl
Writing src/thrift-hive_sup.erl
$ mkdir deps
$ mkdir rel
$ mkdir include
Thriftのライブラリはthrift-erlを使います。rebar.configは以下のとおりです。
rebar.config:
1sub_dirs, ["rel"]}.
2
3{deps_dir, ["deps"]}.
4{erl_opts, [debug_info]}.
5
6{deps, [
7 {thrift_erl, "0.5.0", {git, "git://github.com/xslogic/thrift_erl", "HEAD"}}
8]}.
thrift-erlをrebar get-depsで取得します。
$ rebar get-deps
==> thrift-hive (get-deps)
Pulling thrift_erl from {git,"git://github.com/xslogic/thrift_erl","HEAD"}
Cloning into 'thrift_erl'...
==> rel (get-deps)
==> thrift_erl (get-deps)
生成したコードのうち、.erlをsrcフォルダへ、.hrlをincludeフォルダへコピーします。
$ mv gen-erl/*.hrl include/
$ mv gen-erl/*.erl src/
生成したコードを呼び出すモジュールを作成します。
$ rebar create template=simplemod modid=thrift_hive
==> rel (create)
Writing src/thrift_hive.erl
Writing test/thrift_hive_tests.erl
==> thrift_erl (create)
Writing src/thrift_hive.erl
Writing test/thrift_hive_tests.erl
==> thrift-hive (create)
Writing src/thrift_hive.erl
Writing test/thrift_hive_tests.erl
なぜかrelとthrift-erlの下にも生成されてしまいました…。このままコンパイルしてみると、失敗します。
$ rebar create template=simplemod modid=thrift_hive
==> rel (create)
Writing src/thrift_hive.erl
Writing test/thrift_hive_tests.erl
==> thrift_erl (create)
Writing src/thrift_hive.erl
Writing test/thrift_hive_tests.erl
==> thrift-hive (create)
Writing src/thrift_hive.erl
Writing test/thrift_hive_tests.erl
$ rebar compile
ERROR: One or more .beam files exist that are not listed in thrift_erl.app:
* thrift_hive
ERROR: compile failed while processing /home/masayuki/work/erlang/thrift-hive/deps/thrift_erl: rebar_abort
rel/thrift_erlの下に作成されたファイルを削除したところ、コンパイルが完了しました。 Hiveに接続するコードは以下のとおりです。
thrift_hive.erl:
-module(thrift_hive).
-include("hive_service_types.hrl").
-include("thriftHive_thrift.hrl").
-export([my_func/0]).
my_func() ->
{ok, C0} = thrift_client_util:new("localhost", 10000, thrift_hive, []),
thrift_client:call(C0, execute, ["select c1, count(*) from test group by c1"]),
ok.
Hiveに接続するコードをeunitのテストケースから実行します。
thrift_hive_tests.erl:
1-module(thrift_hive_tests).
2-include_lib("eunit/include/eunit.hrl").
3
4connect_test() ->
5 thrift_hive:my_func().
テストを実行します。
$ rebar eunit suites=thrift_hive
==> rel (eunit)
==> thrift_erl (eunit)
There were no tests to run.
==> thrift-hive (eunit)
Compiled src/thrift_hive.erl
thrift_hive_tests: connect_test (module 'thrift_hive_tests')...*failed*
in function thrift_client_util:new/4
called as new("localhost",10000,thrift_hive,[])
in call from thrift_hive:my_func/0 (src/thrift_hive.erl, line 9)
in call from thrift_hive_tests:connect_test/0
**error:undef
=======================================================
Failed: 1. Skipped: 0. Passed: 0.
ERROR: One or more eunit tests failed.
ERROR: eunit failed while processing /home/masayuki/work/erlang/thrift-hive: rebar_abort
エラーになりました。どうもconnectionを取得するところがうまくいってないようです。
Conclusion
.thriftからのコード生成は簡単にできたのですが、Erlangで動かすところで躓いてしまっています。まだ生成したコードとthrift_erlのAPI呼び出しがどう連携するのかも分かっていません。Erlangのコードをちょっとずつ見ていきながら、原因を確認していきます。