embulk の plugin を kotlin で実装する 

## はじめに

こんにちは。データエンジニアリンググループでエンジニアをやっている[@bandwagondagon](https://twitter.com/bandwagondagon)です。
[CETというプロジェクト](https://qiita.com/_stakaya/items/21d6e8b9c1f7c8529e2f#%E3%81%AF%E3%81%98%E3%82%81%E3%81%AB)で、分析をしたり機械学習活用のためのデータ基盤を開発したりしています。

業務の中で embulk を使ってETL()開発することがあり、その際に[embulk-input-bigquery](https://github.com/medjed/embulk-input-bigquery) という plugin にPRを出すことが何度かありました。

そこで

- embulk-input-bigquery において以下のようなパフォーマンス面での問題点
  - [Bigquery::QueryData#all is very slow](https://github.com/google/google-api-ruby-client/issues/627)
  - [google-api-ruby-client で BigQuery にクエリを投げるとメモリを大量に喰って憤死する](https://qiita.com/sonots/items/1271f3d426cda6c891c0)
  - [embulk で pure java と jruby でプラグインを作ったときの速度比較](https://qiita.com/sonots/items/82775b33332eb5215abc)
- 上記のパフォーマンス問題を  java 実装にすることで解決できるのではないかと思った
- せっかくなら、近年盛り上がりを見せている kotlin で plugin を書いてみたかった

という問題や想いが生まれたので、個人的に embulk-input-bigquery を kotlin で書き直してみることにしました。
本記事ではそこで得た知見として

- embulk の plugin を kotlin で書くための方法
- 実際に kotlin で実装した plugin [embulk-input-bigquery-kotlin](https://github.com/ykoyano/embulk-input-bigquery-kotlin) 

を紹介しようと思います！

## embulk とは？

embulk とは plugin 機構による柔軟なETL処理を実現している OSS の並列バルクデータローダーです。

- [githubレポジトリ](https://github.com/embulk/embulk)
- [公式Document](http://www.embulk.org/docs/#)

plugin は

- input-plugin (入力のデータソース)
- filter-plugin (データ加工)
- output-plugin (データの出力先)

の3種類に分けられ、 [jruby](http://jruby.org/) と java の好きな言語で書くことができます。

(もっと embulk について詳しく知りたいという方には[@hiroysato](https://qiita.com/hiroysato)さんの[Fluentdのバッチ版Embulk(エンバルク)のまとめ](https://qiita.com/hiroysato/items/397f36c4838a0a93e352)が、2015年のリリース当時の情報から直近の勉強会での発表情報まで非常に詳しく関連情報をまとめられているのでオススメです。)

## embulk の plugin で kotlin で書くための方法

### 事前に必要なこと

- embulk の[インストール](https://github.com/embulk/embulk#quick-start)
- kotlin の[インストール](https://kotlinlang.org/docs/tutorials/command-line.html)
- gradle の[インストール](https://gradle.org/install/)

### STEP1 : java で plugin を書くための設定

`embulk mkbundle` コマンドで生成される plugin のひな形は ruby で記述されているため、まず java で plugin を開発できる準備をします。

java で ビルドツールとして gradle を用いるのが一般的でしょうか。

[embulk-input-jdbc](https://github.com/embulk/embulk-input-jdbc) の [build.gradle](https://github.com/embulk/embulk-input-jdbc/blob/master/build.gradle) などを参考にタスク定義を書くのも良さそうですが、ここでは

 [@kamatama41](https://github.com/kamatama41) さんの [gradle-embulk-plugin](https://github.com/kamatama41/gradle-embulk-plugin) を使用したいと思います。

 [gradle-embulk-plugin](https://github.com/kamatama41/gradle-embulk-plugin) は

- [embulk-core](https://bintray.com/embulk/maven/embulk/view) の依存関係
- 新規plugin作成時のひな形生成から[rubygems](https://rubygems.org/)へのpushなど、pluginの開発・公開に必要な[様々なタスク](https://github.com/kamatama41/gradle-embulk-plugin#tasks)の定義

などといった`build.gradle` の設定をラップしてくれているため、以下のように`build.gradle` の設定を非常に簡潔にすることができます。

```groovy
buildscript {
    repositories {
        mavenCentral()
        maven { url 'http://kamatama41.github.com/maven-repository/repository' }
    }
    dependencies {
        classpath "com.github.kamatama41:gradle-embulk-plugin:0.1.4"
    }
}

apply plugin: "com.github.kamatama41.embulk"
repositories {
    mavenCentral()
    maven { url 'http://kamatama41.github.com/maven-repository/repository' }
}

embulk {
    version = "0.8.39"
    category = "input"
    name = "embulk-input-bigquery-kotlin"
    authors = ["Yuji Koyano"]
    email = "yuji.koyano.800@gmail.com"
    description = "embulk input plugin from bigquery written by kotlin."
    licenses = ["MIT"]
    homepage = "https://github.com/ykoyano/embulk-input-bigquery-kotlin"
}
```

また,   gradle-embulk-plugin では java での plugin テンプレートを生成する [newPlugin](https://github.com/kamatama41/gradle-embulk-plugin#tasks)というタスクをサポートしているため、これですぐに java で plugin の開発を始めることができます。

### STEP2 : kotlin で plugin を書くための設定

前述の gradle-embulk-plugin を使用すれば kotlin の導入も非常に簡単に行うがことができます。

```groovy

buildscript {
    ext.kotlinVersion = '1.2.10' # 記事執筆時点で最新
    repositories {
        mavenCentral()
        jcenter()
        maven { url 'http://kamatama41.github.com/maven-repository/repository' }
    }
    dependencies {
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion"
        classpath "com.github.kamatama41:gradle-embulk-plugin:0.1.4" # 記事執筆時点で最新
        classpath "net.researchgate:gradle-release:2.6.0" # 記事執筆時点で最新
    }
}
```

これだけで kotlin での embulk plugin の開発準備は完了しました。

 

参考記事

- [gradle-embulk-pluginを作った #Embulk](http://kamatama41.hatenablog.com/entry/2017/03/21/135412)
- [EmbulkのプラグインをKotlinで書く](http://kamatama41.hatenablog.com/entry/2017/02/05/165729)



## embulk-input-bigquery-kotlin

上記の `build.gradle` 設定を利用して、input ソースとして bigquery を扱う kotlin 実装の plugin[embulk-input-bigquery-kotlin](https://github.com/ykoyano/embulk-input-bigquery-kotlin) を作りました。

[embulk-input-bigquery](https://github.com/medjed/embulk-input-bigquery) と比較してまだまだBQに関して設定できる項目や少なかったり、`resume`・`cleanup` タスクや一部のデータ型に対応していないなど未完成な部分はありますが、今後対応していく予定です。

### jruby 実装と kotlin実装 の比較

Bigquery の public data を用いて embulk-input-bigquery と embulk-input-bigquery-kotlin の読み込みのパフォーマンスを比較してみました。

速度比較に使った task 設定の yaml は以下の通りです。

##### embulk-input-bigquery-kotlin
```yaml
in:
  type: bigquery
  project: <PROJECT_NAME>
  columns:
    - {name: id, type: long}
    - {name: author, type: string}
    - {name: time, type: long}
    - {name: text, type: string}
    - {name: parent, type: long}
  asynchronous_method: true
  cache: false
  standard_sql: true
  sql: "SELECT id, author, time, text, parent FROM `bigquery-public-data.hacker_news.comments` LIMIT 10000"

out:
  type: stdout
```

##### embulk-input-bigquery-kotlin
```yaml
in:
  type: bigquery-kotlin
  columns:
    - {name: id, type: INT64}
    - {name: author, type: STRING}
    - {name: time, type: INT64}
    - {name: text, type: STRING}
    - {name: parent, type: INT64}
  sql: "SELECT id, author, time, text, parent FROM `bigquery-public-data.hacker_news.comments` LIMIT 10000"

out:
  type: stdout
```

### timeコマンドで計測した処理時間(秒)の結果

|      | embulk-input-bigquery | embulk-input-bigquery-kotlin |
| :--: | :-------------------: | :--------------------------: |
| real[s] |         22.79         |            11.49             |
| user[s] |         47.01         |            17.90             |
| sys[s]  |         1.39          |             0.80             |

実装を jruby から kotlin に変更するだけで、2倍近く処理時間に差が出ることが確認できました。

## 終わり

以上 
- embulk の plugin を kotlin で書くための方法
- 実際に kotlin で実装した plugin [embulk-input-bigquery-kotlin](https://github.com/ykoyano/embulk-input-bigquery-kotlin) 
  の紹介でした。

embulk は jruby と java といった異なる言語での plugin 実装をサポートしているため、言語選択といった意味では、コントリビュートへの敷居　が低い OSS であると考えています。
また本記事で紹介したように kotlin での plugin 開発という選択肢も非常に面白いと思いますので、これ機会にぜひ皆さんも embulk の plugin 開発に参加してみて下さい！
また、もっとこう書いたらよいよ、などといったお話をコメントにてお待ちしております〜！