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のコードをちょっとずつ見ていきながら、原因を確認していきます。