Amazon AthenaをAWS Lambdaから操作できるようにしてみた

Amazon Athenaがリリースされてから、できるだろう、というイメージがあったのでやってみました。現在jdbc接続しかできなさそうなので、Lambdaは必然的にJavaで記述することになります。

# 結果
結果だけ見たい人はこちらをどうぞ。ただ、「とりあえず動く」コードになっています。

https://github.com/kojiisd/aws-lambda-athena

# 制限事項
1. 取得している値は全て文字列として扱っています。他の型で受け取りたい場合はソースを修正する必要があります。
2. 見ればわかりますが、適切な例外の処理などはほとんどないです（これが「とりあえず動くコード」の原因）。きちんと使いたい場合はご自身で足りない分を実装してください。

# やりたいこと
1. Amazon AthenaにLambdaからアクセスできるプログラムの作成
2. できるだけ柔軟に欲しい情報を取れるようにしたい

結果として、入力用JSONに期待するカラムも記述するようにしました。

# 準備
## Credentialの準備
src/main/resources/config/credentialのファイルの中身を埋めてください。AthenaとLambda実行の権限があるアクセスキーを記述すれば大丈夫だと思います。

## 媒体作成
以下のコマンドで依存ライブラリを含んだjarファイルを作成してください。

```
$ mvn clean package
```
この時"clean"オプションはつけてください。pom.xmlを見ればわかりますが、Athenaのjarファイルを成果物に含めるために以下の設定をしています。

```pom.xml
<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-install-plugin</artifactId>
  <executions>
    <execution>
      <id>install-external</id>
      <phase>clean</phase>
      <configuration>
        <file>${basedir}/lib/AthenaJDBC41-1.0.0.jar</file>
        <repositoryLayout>default</repositoryLayout>
        <groupId>com.amazonaws</groupId>
        <artifactId>aws-lambda-athena</artifactId>
        <version>1.0</version>
        <packaging>jar</packaging>
        <generatePom>true</generatePom>
      </configuration>
      <goals>
        <goal>install-file</goal>
      </goals>
    </execution>
  </executions>
</plugin>
```
## Lambdaへのアップロード
作成したjarファイルをLambdaにアップロードしてください。この時Jarのファイルサイズが10MBを超える大きなものになると思うので、LambdaへのアップロードはS3を使った方が便利です（Lambdaもそう推奨しています）

## 入力用のJSONの準備
今回入力としては以下のパラメータを想定しています。

|No|Column Name|Description|Required|
|-----------:|:------------|:------------|:------------|
|1|region|リージョン名|false|
|2|s3Path|SQLを保持する先のS3パス|true|
|3|sql|SQL|true|
|4|columnListStr|取得したいカラムのリスト（カンマ区切り）|true|

リージョンは現在us-east-1かus-west-2のみ対応しています。Lambdaでテストする場合は上記を含んだJSONを送ってあげれば実施できます。リージョンを指定する場合は、S3のバケットのリージョンと接続先Athenaのリージョンを合わせるようにしてください。

```JSON例
{
  "region": "us-east-1",
  "s3Path": "s3://ishida-athena-staging-dir/",
  "sql": "SELECT elbname, requestip,  requestport, backendip, backendport, requestprocessingtime, backendprocessingtime, timestamp FROM sampledb.elb_logs order by timestamp desc limit 10",
  "columnListStr": "elbname, requestip,  requestport, backendip, backendport, requestprocessingtime, backendprocessingtime,  timestamp"
}
```
実施する際のLambdaのスペック設定ですが、メモリは少なくとも192MB,タイムアウトは30秒ほどは見ておいた方が良いかと思います。メモリ128MBで実施したらOutOfMemoryが出てしまいました。（デフォルトだとメモリ512MB、タイムアウト15秒だったかな。それはそれで今回は不適切です）

# 実行
Lambdaスクリプトを実行してみます。今回アクセス先は「sampledb」にします。Athenaを利用開始するとデフォルトで設定されるアレです。

まず、今回実施しようとしているクエリをそのままAthenaで叩いてみます。

<img width="1103" alt="スクリーンショット 2016-12-23 11.45.07.png" src="https://qiita-image-store.s3.amazonaws.com/0/102769/efc044a5-c377-6d8a-8c43-5a4ef8a8bc1b.png">

それでは準備していたスクリプトをLambda上で実行します。

<img width="1300" alt="0e72db35-9f83-1b56-4c28-8739d3277ea4.png" src="https://qiita-image-store.s3.amazonaws.com/0/102769/54aa4609-d665-b993-b97a-3d3c32ff66f5.png">

とりあえず何かしら値が取れたようですが、Lambdaのコンソールは見づらい。。。

CloudWatchでログ内容を確認します。（というかLambda処理に20秒もかかってますね。。。初回実行とはいえなかなか厳しい性能です＾＾；）
あとLogger周りでWARNログが出ていますが、ここでは気にしません。

```CloudWatch
Input parameter:jp.gr.java_conf.kojiisd.lambdaathena.dto.Request@6e38921c[
region=us-east-1
s3Path=s3://ishida-athena-staging-dir/
sql=SELECT elbname, requestip, requestport, backendip, backendport, requestprocessingtime, backendprocessingtime, timestamp FROM sampledb.elb_logs order by timestamp desc limit 10
columnListStr=elbname, requestip, requestport, backendip, backendport, requestprocessingtime, backendprocessingtime, timestamp
]

result:
elbname, requestip, requestport, backendip, backendport, requestprocessingtime, backendprocessingtime, timestamp
lb-demo,246.140.190.136,63777,250.193.168.100,8888,7.2E-5,0.379241,2014-09-30T01:28:17.587188Z
lb-demo,246.129.63.235,10182,251.36.119.81,8888,4.7E-5,0.013368,2014-09-30T01:18:08.310452Z
lb-demo,248.214.120.18,25187,249.205.250.232,8000,5.9E-5,0.014451,2014-09-30T01:18:08.294613Z
lb-demo,249.125.138.56,64948,241.35.85.250,8888,6.6E-5,0.023272,2014-09-30T01:18:08.266146Z
lb-demo,247.161.61.55,2700,254.243.154.128,8888,7.5E-5,0.026167,2014-09-30T01:18:08.264297Z
lb-demo,241.35.85.250,10182,248.214.120.18,443,4.6E-5,0.013153,2014-09-30T01:18:08.225343Z
lb-demo,250.165.161.74,25187,249.45.101.192,8888,4.8E-5,0.013252,2014-09-30T01:18:08.207854Z
lb-demo,247.161.61.55,2700,248.14.80.185,8888,4.8E-5,0.015274,2014-09-30T01:18:08.175776Z
lb-demo,249.45.101.192,64948,247.161.61.55,8888,1.15E-4,0.020176,2014-09-30T01:18:08.162703Z
lb-demo,253.51.141.83,10182,243.16.92.84,8888,6.6E-5,0.015285,2014-09-30T01:18:08.130488Z
```

なんとなくそれっぽい結果が出ています。一応Excelに貼り付けてみると、、、

<img width="811" alt="スクリーンショット 2016-12-23 12.18.04.png" src="https://qiita-image-store.s3.amazonaws.com/0/102769/7cfd9164-bf17-46e7-7135-97800b57da45.png">

件数も並び方も一緒ですね、素晴らしい！

# まとめ
AthenaにLambdaからアクセスすることができるようになりました。が、見ての通り処理は重いしコストがかかりまくりですね。このレベルであればPythonとかNode.jsでサクッと書きたいものです。Athenaの接続が他の言語からもできるようになることを期待しています。

今回書いたコードを応用すれば、CRUDはもちろん、複数の型の取得に対応させることも可能ですので、もしやる気が出れば（笑）次回そちらもやってみようかと思います。