はじめに
昔のLambdaは制限時間が5分で、batch系の処理なんかでは制限時間をオーバーして異常終了したりしていました
またAPI Gatewayから起動されるLambdaが「もっと速く出来ないの」って言われたりもします
その時の対応内容を紹介します
エンジニアですので、数値を持って語るべきなのですが、前職での実行内容だったり、手元に環境がなかったり、この記事のためだけに環境を作るのは諸々の事情で憚られますので割愛します
ちょっと寄り道
レスポンスで問題が出た場合は、まずprofileを取る訳ですが、私がいつもやる方法も念の為記しておきます
対象ライブラリ
import cProfile
import pstats
cProfileだけでprofileは取れますが、取得結果をソートするためにpstatsも使います
測定開始
cProfileをインスタンス化して、測定の有効化
pr = cProfile.Profile()
pr.enable()
測定終了と結果確認
profileを無効化してソート用objectを生成
ソートして出力
pr.disable()
stats = pstats.Stats(pr)
stats.sort_stats('tottime')
stats.print_stats()
どんな項目でソート出来るかは、マニュアルを参照してください
profileに目立ったボトルネックが見つからない場合もあると思います
その場合はI/Oが多そうな所を改善したら良い結果に繋がるケースもありました
本題
psycopg2のDictCur
listでなくdictで検索結果を参照できるのはすごく便利なのですが、dictへのマッピングコスト高いのか、大きなボトルネックになっていました
対象のテーブルがかなりのカラム数だったことも影響していそうです
DictCurを諦め、代わりにlistの添字を定数で抽象化して回避しました
参考Url: Python psycopg2 で dict形式で結果を取得する
logger
LambdaはprintすればCloudWatch Logsでlogを見られる訳ですが、問題となったLambdaはloggerを使っていました
loggerの設定って闇が深いみたいでこちらの目的はlogを見たいだけなんですよね
loggerをprintにしただけで、処理速度が改善しました
logger的な出力レベルのコントロールもしたかったので、ログ出力をラップする簡単な関数を作ってloggerに必要な機能を損ねないようにしました
また、単純にprintを減らしただけで処理速度が改善したケースもありました
logは本番運用では必要となってきますが、開発でしか必要としないlogを出しっぱなしなケースもあると思います
本当に必要なlogのみを出力するようにしたいものです
キャッシュ1
profileを取った時にboto3が目立っていました
だからといってSDKを使わない訳にはいきません
なので、1度生成したboto3 objectはキャッシュして2回目以降はキャッシュを使うようにしました
結構大きなLambdaで、複数ファイル、10個以上のclassで構成されていました
classのプロパティに生成したboto3 objectを保持するようにしました
if self.__XXXX_table_obj is None:
self.__XXXX_table_obj = boto3.resource("dynamodb").Table(self.get_XXXX_table())`
また、マスターの参照結果もプロパティにキャッシュするようにしました
if XXXX_id in self.__XXXX_cache:
return self.__client_cache[XXXX_id]
なお、頻繁にアクセスされるアプリではないため、Lambdaコンテナの再生成されるタイミングでマスタも新規に取得できます
キャッシュ2
何をどうやっても目標の時間が達成できない場合もあるかと思います
問題となったシステムは、一定時間ごとにまとまってデータが更新されるものでした
ですので、データが更新されない間は同じ内容を返せば良いことになります
そのため、処理結果(Json)をDynamoDBに登録してその内容を返すようにしました
キーはAPI名+パラメータとしました
API体系としてはシンプルでしたので、問題ありませんでした
データが更新された際には、SNSで通知が来るのでDynamoDBの内容を登録しなおします
1つ誤算だったのはDynamoDBに格納できる最大値(400KB)をオーバーするJsonがあったことでした
繰り返し項目が多いJSONだったので、圧縮してバイナリで登録することで事なきを得ました
終わりに
小ネタばかりで恐縮ですが、この記事がPython Lambdaの処理速度に悩める方の一助になると幸いです