Lambda の同時起動を S3 に置いたロックファイルで制御する

CloudWatch スケージュールで定期的に Lambda を実行する場合、稀に同時実行されてしまう場合があるようです。
それを防ぐために、S3 にロックファイルを作成することで制御してみようと思います。

同時実行数での制御

Lambda には 同時実行数 という設定項目があり、同時実行の予約数を制限することができます。
じゃあ、この値を 1 にすればそれで解決するのでは? と思ったんですが、どうやら 1 に設定した場合でも、関数の実行方法やユースケースによっては同時実行されてしまうケースがあるようです。

参考

S3 にロックファイルを設置して制御する

Lambda の同時実行を防止する方法としては、次のような方法が見つけられます。

  • DB に実行状態を保持する
  • 実行中であることがわかるファイルを設置する

今回は後者の方法で同時実行を制御してみます。

S3 バケットの作成

ロックファイルを設置するバケットを作成します。今回は lambda-test というバケットとします。

関数の準備

ランタイムは Python 3.6 とします。

必要な処理としては、ロックファイルの 作成削除存在有無を確認 の3 種類です。次のように実装します。

import boto3
BACKET = 'lambda-test'
LOCK_FILE = 'running_lock'

def create_lock_file():
    print('create lock file.')
    s3 = boto3.resource('s3')
    s3obj = s3.Object(BACKET, LOCK_FILE)
    s3obj.put(Body='running...')


def delete_lock_file():
    print('delete lock file.')
    s3 = boto3.resource('s3')
    s3obj = s3.Object(BACKET, LOCK_FILE)
    s3obj.delete()


def is_running():
    s3 = boto3.resource('s3')
    try:
        s3.ObjectSummary(bucket_name=BACKET, key=LOCK_FILE).load()
    except Exception as e:
        return False

    return True


def lambda_handler(event, context):
    if is_running() is True:
        print('Process is running.')
    else:
        create_lock_file()

        # do something ...

        delete_lock_file()

今回、S3 のバケット内に対象のオブジェクトが存在するかどうかを ObjectSummary() でチェックしていますが、他にもチェック方法はたくさんあるようです。

とりあえずこれで同時実行は防げそうです。

comments powered by Disqus