EMR+Prestoこまごま

EMR+presto関連の細かい設定に関して。
Emr5.1.0で検証しています。
Emrと関係ない話も多分に含まれます。

###サクっと
とりあえずちゃっちゃかやりたい人は
http://qiita.com/mix/items/887c545daf5491eed552
ここをどうぞ！

###ソフトウェア設定の編集
・hiveのmetastoreを外部のmysqlへ(例はRDS)
・presto-configの一部の設定
・mysql.propertiesの追加

```
[
  {
    "Classification": "hive-site",
    "Properties": {
      "javax.jdo.option.ConnectionUserName": "Mysqlユーザ",
      "javax.jdo.option.ConnectionPassword": "パスワード",
      "javax.jdo.option.ConnectionURL": "jdbc:mysql://[[rdsのエンドポイント]]:[[ポート]]/hive?createDatabaseIfNotExist=true",
    },
    "Configurations": []
  },
  {
    "Classification": "presto-config",
    "Properties": {
      "query.max-memory": "5GB",
      "query.max-memory-per-node": "1GB",
      "query.queue-config-file":"/home/hadoop/presto-queues.json"
    },
    "Configurations": []
  },
  {
    "Classification": "presto-connector-mysql",
    "Properties": {
      "connection-url": "jdbc:mysql://[[rdsのエンドポイント]]:[[ポート]]",
      "connection-user": "Mysqlユーザ",
      "connection-password": "パスワード"
    },
    "Configurations": []
  }
]
```

上記内容を記述したファイルをs3に置いてs3パスを指定する、もしくは
この内容をテキストエリアにぶち込むと良いです。

S3に置く場合の注意点は、Emrを起動したいリージョンと、設定ファイルを設置するS3バケットのリージョンが違う場合、起動ボタン押下時点で301 ("moved permanently")が返るせいで起動できません。

### Mysqlカタログの複数設定
結論から言うとEmrを利用する以上は無理なのではないかと思います。
まず、カスタムスクリプトが走る段階だとprestoがインストールされていないため、

/etc/presto/conf/catalog

ディレクトリが生成されておらず。スクリプトの中で無理矢理生成すると、
presto-server起動時に何かしら失敗するらしく、Emrクラスタ自体がブートストラップエラーで起動しませんでした。
その際の失敗原因のログは多分とれないのではないかと･･･ (puppetとかいうchefみたいなツール？のエラーログがあるだけで、何故失敗したのかの直接的原因のログは恐らく残るようになっていないように思います)

全ノードに手動で設置すればあるいは･･･？とも思いますが検証まではしていません。
presto-adminを利用すれば出来るのかな？

###query.queue-config-file
query.max-concurrent-queries
query.max-queued-queries

この2つの設定値を触るために利用します。直接書いてもいけるっぽいですが、公式いわく非推奨だそうで、
query.queue-config-fileを使って欲しいそうです。

maxConcurrent = クエリの同時実行数。これを過ぎてクエリ発行しようとすると状態がQueuedとなり、現在runningであるクエリがどれか1つ終わるのを待ってから実行が開始されます。

maxQueued = Queueをいくつまで受け付けるか。この値を超えてQueuedクエリを発行しようとすると、 Too many queued queries! と即座に返りクエリを受け付けてくれません。なお、runningのクエリはこれに含まれないようです。

maxQueuedは0を設定できますが、maxConcurrentは0だとクエリが発行できなくなります。起動自体に成功するかはすいません忘れました

設定例を以下に

```
{
  "queues": {
    "user.${USER}": {
      "maxConcurrent": 5,
      "maxQueued": 5
    },
    "muscle": {
      "maxConcurrent": 1,
      "maxQueued": 2
    }
  },
  "rules": [
    {
      "user": "muscle",
      "queues": ["muscle"]
    },
    {
      "queues": ["user.${USER}"]
    }
  ]
}
```

user.${USER}はクエリ実行ユーザ名に自動変換されるようです。

ドキュメントによると「ルールが見つかった順」らしいので、
この場合だと、クエリ実行ユーザがmuscleだった場合は同時実行クエリ数1の設定が使用され、muscle以外のユーザなら同時実行クエリ数5の設定が使用されます。

rubyとかで慣れてる人はやってしまいがちだと思いますが、このjsonファイルは尻の余計なカンマをつけないようにしましょう。
つけるとjacksonがパースエラーを吐き、EMRの起動に失敗します。

ここの設定を細かく確認しながらやりたい場合は適宜json書き換えつつ、

```
sudo restart presto-server
```

を実行すれば反映されます。多分これはMasterノードだけがこの設定を参照するからなんでしょうね。
taskノードも設定を持つ必要があるようなもの(例えばcatalog)はrestartしてもtaskノードに分配とかまではしないため、
恐らくクラスタを起動しなおすしかないと思います。
mysqlカタログ複数設定でも書きましたが、全ノードに手動で設置すればあるいは･･･？

###presto-queues.json
上記例のような記述をしたファイルをs3あたりに置いておき、ブートストラップアクションでquery.queue-config-fileに指定したパスに設置するようなシェルを指定します。

シェル例

```
#!/bin/bash -ex

cd ~

aws s3 cp s3://[[bucket]]/[[key]]/presto-queues.json ./
```

EMRは起動にミスったりしたとき割と追いづらいので、-exつけといたほうが良い場合が多いです。
実行ユーザは基本的にhadoopになる模様です。

###jvm関係
Emrはjvmの設定を少なくとも正規の手段では触れないようです。
(http://docs.aws.amazon.com/ja_jp/ElasticMapReduce/latest/ReleaseGuide/emr-presto.html)
presto-adminを使えばEMRでもいけるのかな？
Emr+presto+presto-adminは公式に資料があるので興味のある方はぜひとも検証を。

最大ヒープサイズ(Xxm)は  /etc/presto/conf/jvm.config で確認できます。(勿論ps auxでも)
これはインスタンスのメモリ量によってEmr側が適宜設定を変えてくれているようで、大体インスタンスメモリ量の90%程度が設定されているようです。

###query.max-memory
クエリ全体で利用できるメモリの最大量だと思われます。

###query.max-memory-per-node
1クエリに対して1workerが使用できるメモリの最大量だと思われます。
JVMのヒープサイズの40%程度までしか設定できません。
それを超えて設定しようとすると

```
query.max-memory-per-node set to XXGB, but only XXB of useable heap available
```

というようなjavaの例外が出てpresto-serverの起動に失敗します。(EMRの起動そのものは成功する)
XXは適宜読み替えてください。

###query.initial-hash-partitions
GROUP BY aggregations are now distributed across a fixed number of machines. This is controlled by the property query.initial-hash-partitions set in etc/config.properties of the coordinator and workers. If the value is larger than the number of machines available during query scheduling, Presto will use all available machines. The default value is 8.

group by集計は固定数のマシンに振り分けられて、それのデフォルト値は8台。node数が8より小さいときは全部使う らしいです。デフォルト8個まで、の理由はapacheのコネクションプールライブラリのデフォルト設定を考慮しているとどこかで見た覚えがあります。
このあたりはそこまで深く追っていないので有志を待ちたいところです。

###2つのクエリが実行されている時、片一方のクエリのメモリ不足で片一方は落ちる？
落ちませんでした。クエリベースでなくタスクベースだったときは落ちたらしいと小耳に挟みました。
なお、メモリ不足が発生した時は

```
Query exceeded local memory limit of XXGB
```

こんなのが出ます。query.max-memoryに引っかかったのかquery.max-memory-per-nodeに引っかかったのかで若干メッセージが変化するようです。上記は確かquery.max-memory-per-nodeに引っかかった時なはず。

### 一度に大量のクエリが流れて、いっぺんにメモリを食うような状態になったら？
Query exceeded...が出るようなクエリを10個いっぺんに流したところ、

```
Encountered too many errors talking to a worker node. The node may have crashed or be under too much load. This is probably a transient issue, so please retry your query in a few minutes. 
```

```
Query is gone (server restarted?)
```

```
No nodes available to run query
```

こんなのが出ました。ほとんど追っていないのであまり参考にしないでください
ただ、クラスタというかEc2インスタンスそのものが死ぬようなことにはなりませんでした。

### node-scheduler.min-candidates
minCandidates の min を信じるのであれば、「少なくともminCandidates の値以上の台数のworkerノードを候補として選出、その中から暇そうなworkerに割り当てる」という感じの挙動と推測します。
なのでこの値を大きくしたからといってパフォーマンスアップには繋がらないのではないかな･･･？と思います。
(突っ込んだ検証まではしていません)

###ログ
Emrでは /var/log/presto/server.log にログが吐かれるようです。

###おわり
途中からほぼEMRが関係なくなりましたが何かの足しになれば。
他、パフォーマンス検証などの話は時間あき次第別記事に書こうと思います。
prestoの実装そのものも追いたいところですね。