Apache Flinkのlack of memory

Flinkにバッチジョブを頻繁に投入していたら、metaspaceが単調増加し遂には lack of memory を起こした。一人でバックエンドを抱えていて他にも対応案件があるので定期的な再起動で逃げていたのだけど、限界を感じたので調査した。そもそも使い方を根本的に誤っていた。

どうやら典型的な問題でDebugging Classloadingに書かれていた。

avoid dynamic loading する必要があった。まずVisual VMでみてみる。

Apache Flinkメモリリーク

how to avoid dynamic loading

対応方法は v1.3.x の時は lib/ 配下にジョブの jar ファイルを配置するだけ。 v1.4.x ではさらに classloader.resolve-orderparent-first を設定する。

下の様にメタスペースの拡張が緩和されて、めでたしめでたし。

Apache Flinkメモリリーク 改善後

ただしFlink立ち上げ時に読まれ更新されないため、ジョブを更新するのにFlinkの再起動が必要になり注意が必要。

Dynamic Loadingについて

Flinkではブートストラップクラスローダー・システムクラスローダーの他にシステムクラスローダーを親に持つFlinkUserCodeClassLoaderが使われている。 これはジョブで使われるクラスをロードするのに使われる、このようなローディングをDynamic Loadingと呼ぶ。

Apache Flinkv1.3.xv1.4.x では挙動が少し異なる。

標準的なJavaでは、親クラスローダーからクラス名の解決が図られる。 v1.3.x では通常通り親クラスローダーで優先的に解決されるが v1.4.x のデフォルトでは逆転し子クラスローダーから優先して解決にあたる。 しかし classloader.resolve-order によって指定されているため、親優先に戻すことも可能。

1
classloader.resolve-order: parent-first

この解決順序と無関係に親クラスローダーが優先されるクラスが存在する。 これらは classloader.parent-first-patterns.default,classloader.parent-first-patterns.additional で指定される。 classloader.parent-first-patterns.default は主にシステムで利用するクラスが含まれている。

v1.4.x では子クラスローダーが優先されるのでjarファイルを置いただけではメモリリークは改善されない。 ドキュメント通り子クラスローダーが優先されるので resolve-order を入れ替えたりなどが必要だった

配置するjar

Apache Beam を利用しているジョブの fatjar を配置する場合、ランナーの依存を明示しないとバージョンがずれてジョブ実行が失敗したので build.gradle に下を追加した。 ここら辺は使ってるバージョンに合わせてよしなに帰る。貼ったのは v1.3.2 時代の内容。

123456789
buildscript {
  ext.beam_version = "2.2.0"
}

dependencies {
    compile group: "org.apache.beam", name: "beam-runners-flink_2.10", version: beam_version
    compile group: "org.apache.flink", name: "flink-runtime_2.10", version: "1.3.2"
    compile group: "io.netty", name: "netty-all", version: "4.0.27.Final"
}
comments powered by Disqus