概要
AWSのApiGatewayとLambdaで構築したPythonプログラム(FastAPI)に対してある程度のボリュームのリクエストを送っていたら、以下のエラーになりました。
error: QueuePool limit of size 5 overflow 10 reached, connection timed out, timeout 30.00
なぜだろう...ということで調査して原因がわかったので、紹介します。
原因と解決方法
原因は、SQLAlchemy側のコネクションプールの上限設定にありました。
結論、以下のようにするとエラーは無くなりました。
max_overflow=30を新たに指定した形です。
engine = create_engine(settings.CONFIG_DATABASE_URI, pool_pre_ping=True, pool_size=10, max_overflow=30)
※上記設定では10+30=40のコネクションを許容
(参考)
max_overflow
pool_sizeで設定した上限に加えて、追加でOPEN可能な接続数を指定するパラメータです。sqlarchemyを利用するアプリケーション側からはpool_size + max_overflowの分だけDBとの接続をOPENすることが可能になります。デフォルト値は10で-1に指定することで無制限になります。引用元:https://dev.classmethod.jp/articles/sqlalchemy-connection-pooling/
max_overflowの値については、DBのスペックやLambda設定(同時実行数など)、実際に飛んでくるリクエスト数とも関係があるので適宜試行して設定されるのが良いと思います。
今回私が使っていたDBは、MySQL RDSのdb.t3.smallを利用しており、DBのmax connectionは143でした。DBの同時接続数を調べてみると、120-140を推移しており、これが原因でたまに接続エラーが発生していたと考えられます。
max_overflowが大きいほど、一時的に大量の接続が発生しやすくなり、全体でDBのmax_connections(上限)を超えやすくなります。
かといってmax_overflowの値を減らしすぎると、「新規接続が乱発される」「プールから取得できる接続数がすぐ上限に達してしまう」ことが懸念されます。
結論、実際に想定されるリクエスト数(+α)で負荷テストしながら調整する必要があると思いました。
ちなみに、pool_sizeとは、プールに常時保持する接続数です。
大きすぎるとDBの接続数上限にすぐ達します。
参考
AWS Lambdaにはタイムアウト設定があります。
これが30秒になっていると、SQLAlchemyのタイムアウト設定も30秒だったので上述のエラーがログに出ずにLambdaが終了してしまうことがあります。Lambdaを1分とかに設定してから調査すると良いと思います。