HiveServer3.1.3をローカルデバッグする

今回、Hive3.1.3をDocker上で立ち上げ、HiveServerにリモートデバッガをアタッチしてHiveServerの処理を詳細にデバッグする環境をセットアップします。開発環境はIntelliJを前提としてます。

公式にHive用のDockerImageを提供するIssueがHive4.0.0で導入されており、この周辺のコードを流用します。

まず、試しに現時点でのmasterをcloneしてきて、README.mdに従ってビルドします。

cd packaging/src/docker
./build.sh -hive 3.1.3

Docker上で動作するHiveServerにデバッガをアタッチするため、HADOOP_OPTSにデバッグエージェンを起動するための環境変数を設定します。HiveServerはJava8で動作するため、address=*:8888のような表記ではなくaddress=8888と記述しないとエラーが起きることに注意してください。

docker run -d -p 10000:10000 -p 10002:10002 -p 8888:8888 --env SERVICE_NAME=hiveserver2 --env HADOOP_OPTS="-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=8888" --name hiveserver2-standalone apache/hive:${HIVE_VERSION}

ここで、起動直後に Schema initialization failed! のエラーが発生してしまうため、修正する必要があります。

hive3.1.3時点でのschematoolの引数と、最新のmasterのschematoolの引数が異なるため、Metastoreの初期化に失敗してしまいます。

上記の行を、以下の行に変更し、Docker Imageを再度ビルドする必要があります。

$HIVE_HOME/bin/schematool -dbType $DB_DRIVER -initSchema

ビルドが完了したら、以下のコマンドを再度実行し、HiveServerを起動します。

docker run -d -p 10000:10000 -p 10002:10002 -p 8888:8888 --env SERVICE_NAME=hiveserver2 --env HADOOP_OPTS="-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=8888" --name hiveserver2-standalone apache/hive:${HIVE_VERSION}

コンテナログを確認すると、Metastoreのschemaの初期化に成功し、デバッグエージェントが8888でListenし、正常にHiveServerが起動したことを確認できます。

+ : derby
+ SKIP_SCHEMA_INIT=false
+ export HIVE_CONF_DIR=/opt/hive/conf
+ HIVE_CONF_DIR=/opt/hive/conf
+ '[' -d '' ']'
+ export 'HADOOP_CLIENT_OPTS= -Xmx1G '
+ HADOOP_CLIENT_OPTS=' -Xmx1G '
+ [[ false == \f\a\l\s\e ]]
+ initialize_hive
+ /opt/hive/bin/schematool -dbType derby -initSchema
SLF4J: Class path contains multiple SLF4J bindings.
SLF4J: Found binding in [jar:file:/opt/hive/lib/log4j-slf4j-impl-2.17.1.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [jar:file:/opt/hadoop/share/hadoop/common/lib/slf4j-log4j12-1.7.25.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation.
SLF4J: Actual binding is of type [org.apache.logging.slf4j.Log4jLoggerFactory]
Listening for transport dt_socket at address: 8888
Metastore connection URL:  jdbc:derby:;databaseName=metastore_db;create=true
Metastore Connection Driver :  org.apache.derby.jdbc.EmbeddedDriver
Metastore connection User:  APP
Starting metastore schema initialization to 3.1.0
Initialization script hive-schema-3.1.0.derby.sql
...
Initialization script completed
schemaTool completed
[WARN] Failed to create directory: /home/hive/.beeline
No such file or directory
+ '[' 0 -eq 0 ']'
+ echo 'Initialized schema successfully..'
Initialized schema successfully..
+ '[' hiveserver2 == hiveserver2 ']'
+ export 'HADOOP_CLASSPATH=/opt/tez/*:/opt/tez/lib/*:'
+ HADOOP_CLASSPATH='/opt/tez/*:/opt/tez/lib/*:'
+ exec /opt/hive/bin/hive --skiphadoopversion --skiphbasecp --service hiveserver2
2023-05-14 06:01:05: Starting HiveServer2
Listening for transport dt_socket at address: 8888
SLF4J: Class path contains multiple SLF4J bindings.
SLF4J: Found binding in [jar:file:/opt/tez/lib/slf4j-log4j12-1.7.10.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [jar:file:/opt/hive/lib/log4j-slf4j-impl-2.17.1.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [jar:file:/opt/hadoop/share/hadoop/common/lib/slf4j-log4j12-1.7.25.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation.
SLF4J: Actual binding is of type [org.slf4j.impl.Log4jLoggerFactory]
Hive Session ID = cbff1bee-ddb2-476b-8912-6b8c02f49266
Hive Session ID = d4327ad9-227c-4fe7-a9ec-cceb6bb26c62
OK

デバッガをアタッチするにあたり、ローカルのコードを動作中のコードと一致させる必要があるので、rel/release-3.1.3 をチェックアウトしておきます。

git fetch origin rel/release-3.1.3
git checkout rel/release-3.1.3

準備ができたので、以下のような設定でIntelliJでデバッガを起動します。

Debugger Setting

問題なくデバッガがアタッチできることを確認してください。

beelineで接続しても良いですが、IntelliJのDatabase ToolはHiveも対応しているのでIntelliJから接続します。

適当にREADME.mdのサンプルクエリを使ってテーブルを初期化し、クエリが動作することを確認します。

show tables;
create table hive_example(a string, b int) partitioned by(c int);
alter table hive_example add partition(c=1);
insert into hive_example partition(c=1) values('a', 1), ('a', 2),('b',3);
select count(distinct a) from hive_example;

あとは、実際にブレークポイントを設定し、クエリを実行してみるだけです。 試しに、org.apache.hadoop.hive.ql.Drivercompileメソッドにブレークポイントを設定して、 select count(distinct a) from hive_example; を実行してみると、そのポイントからデバッグが可能であることがわかります。