LambdaからAuroraに接続する時に注意すること


TL;DR

  • RDS Proxyの発表により、LambdaからAurora(RDS)が利用しやすくなりました。
  • ただし、ORMライブラリが採用するコネクションプールがあれば、無効化されているかの確認を忘れずに。

LambdaからAurora?

LambdaからAuroraに接続することは、長くコネクションの枯渇問題によりアンチパターンとされてきました。
そんな中、昨年のre:Invent2019でRDS Proxyが発表されたことで、今後の採用が増えそうな予感があります。
RDS Proxyが何を解決するかは、なぜAWS LambdaとRDBMSの相性が悪いかを簡単に説明するを読んでいただければと思います。

実際にやってみた

上記のグラフは、あるLambdα関数内部でRDSへの単一の接続を必要とするケースでのCloudWatchで見るDatabaseConnectionsのグラフです。ここで注目すべきは、矢印の直前で約48,000付近まで達している点です。

これに対し、LambdaのInvocationsのグラフでは、特にこれだけのコネクションを必要とするほどの呼び出し回数を示していません。せいぜい1,000未満。

なにが起こったのか?

この時採用していたORMは、Slickでした。これはScala界隈では有名なフルスタックWebフレームワークPlayframeworkでも採用されており、デファクトなORMになりつつあります。このSlickで注意すべきが、configの設定です。

slick {
    profile = "slick.jdbc.MySQLProfile$"
    db {
        driver = "com.mysql.jdbc.Driver"
        url = "jdbc:mysql://url:3306/table"
    }
}

通常のWebアプリケーションの設定では、上記のような設定があれば、その他細かい設定をすることなく、Slickは使い始めることができます。しかし、これで実行を繰り返した場合に、以下のエラーが発生しはじめました。

slick-pool - Connection is not available, request timed out after 1000ms.: java.sql.SQLTransientConnectionException

これは一体何が発生しているのでしょうか。Slickは3.xにおいては、明示的にコネクションプールの無効化をしない場合、内部的にデフォルトでHikariCPをコネクションプールとして採用します。つまり、Lambdaで起動しているにもかかわらず、内部的なコネクションプールであるHikariCPがコネクションをデータベースとの間に持った状態になります。そして、そのコネクションはLambdaのライフサイクルの中で、正常に解放されないままにリークした状態になります。
実際、グラフの矢印よりも後で、以下の設定を反映させるとDatabaseConnectionsは激減し、Innvocationとも大きく乖離することは解消されたことが分かります。

slick {
    profile = "slick.jdbc.MySQLProfile$"
    db {
        driver = "com.mysql.jdbc.Driver"
        url = "jdbc:mysql://url:3306/table"
        connectionPool = disabled
    }
}

まとめ

今回の件は、相当量の負荷をかけ続けたテストをしない限り、事前に発見することはとても難しい問題でした。
RDS Proxyがリリースされることで、今後ますますLambdaからAuroraを利用するケースが増えてくるかもしれません。自身がお使いになるORMライブラリが内部的にコネクションプールを持っていないか、そしてそれは無効化されているか、これらは今回のScala/Slickの組み合わせに限ったことではなく、是非ご確認いただければと思います。