yanagishimaでなぜJDBCを使ってないのか

約2年前にyanagishimaを開発し始めた時、Javaで実装することに迷いは無かった。
理由は単純に僕が一番馴染んでいる言語だから。

Javaで実装している一方でJDBCを使わなかったのには2つの理由がある。

一つ目は性能面を考慮したから。やりたいこととしてはデータとカラム名の両方を取りたい。なぜならそうしないとどのデータがどのカラムに対応するかわかりづらいから。

JDBCを使う場合はデータを取るためには
https://prestodb.io/resources.html#libraries
にあるようにStatement#executeQueryをよんでそのあとはResultSetをごにょればいいです。
下記のように

String sql = "SELECT * FROM sys.node";
String url = "jdbc:presto://localhost:8080/catalog/schema";
try (Connection connection =
        DriverManager.getConnection(url, "test", null)) {
    try (Statement statement = connection.createStatement()) {
        try (ResultSet rs = statement.executeQuery(sql)) {
            while (rs.next()) {
                System.out.println(rs.getString("node_id"));
            }
        }
    }
}

カラム名を取るにはどうするかというと、DatabaseMetaData#getColumns
を呼びます。

これを呼ぶとどうなるかというともう一回prestoにクエリを投げることになります。

どんなクエリかというとこんな感じです。

SELECT TABLE_CAT, TABLE_SCHEM, TABLE_NAME, COLUMN_NAME, DATA_TYPE,
  TYPE_NAME, COLUMN_SIZE, BUFFER_LENGTH, DECIMAL_DIGITS, NUM_PREC_RADIX,
  NULLABLE, REMARKS, COLUMN_DEF, SQL_DATA_TYPE, SQL_DATETIME_SUB,
  CHAR_OCTET_LENGTH, ORDINAL_POSITION, IS_NULLABLE,
  SCOPE_CATALOG, SCOPE_SCHEMA, SCOPE_TABLE,
  SOURCE_DATA_TYPE, IS_AUTOINCREMENT, IS_GENERATEDCOLUMN
FROM system.jdbc.columns

WHERE TABLE_CAT = 'hive' AND TABLE_NAME LIKE '%' AND COLUMN_NAME LIKE '%'
ORDER BY TABLE_CAT, TABLE_SCHEM, TABLE_NAME, ORDINAL_POSITION

presto-jdbcのソース的にはこの辺
https://github.com/prestodb/presto/blob/b316dbbe9edb281a2bede6ab08c0249586197f1e/presto-jdbc/src/main/java/com/facebook/presto/jdbc/PrestoDatabaseMetaData.java#L963

ようはデータとカラム名の両方を取るためにはprestoに2回クエリを投げる必要があります。
しかもカラム名を取るクエリは意外と遅いです。僕の環境だと10秒ぐらいかかります。

それがあってyanagshimaではJDBCを使っていません。presto-cliのコードを参考に直接JSON APIをたたいています。


2つ目の理由はJDBCを使うとクエリのキャンセルができないからです。
これはpresto-jdbcがStatement#cancelを実装していないからです。
https://github.com/prestodb/presto/blob/07f430b6f27371d5b53ab2795016b810f0f67ab3/presto-jdbc/src/main/java/com/facebook/presto/jdbc/PrestoStatement.java#L170

yanagishimaではクエリのキャンセルのためにHTTP DELETEを投げてます。
これはpresto web uiでキャンセルするときと同じです。

この辺のHTTP API仕様を下記にも書かれています。
https://github.com/prestodb/presto/wiki/HTTP-Protocol

なおZeppelinだとpresto JDBCを使うのでデータとカラム名を取るために2回のリクエストを投げ、そしてクエリのキャンセルはできません。