Lambda でアトミックなロック処理を実装する
Posted on
- #aws
Lambda はステートレスで冪等性のある処理に向いていますが、処理によっては排他制御を必要とする場合があります。排他制御にはアトミックなロック処理が欠かせませんが、Lambda のアーキテクチャでは少し工夫が必要です。
というのも、Lambda 関数が実行されるインスタンスは常に再利用されるとは限らないため、ロックの状態は Lambda の外で管理しなければなりません。
DynamoDB をロックマネージャーとして使う
いろいろ調べたところ、DynamoDB の条件付き書き込みを使うのが良さそうです。
条件付き書き込みを使用すると、オペレーションが成功するのは、項目の属性が 1 つ以上の想定条件を満たす場合のみです。それ以外の場合は、エラーが返されます。
@imaifactory さんの Qiita 記事「DynamoDB をロックマネージャーとして使う」がとてもわかりやすいです。ロックの状態を DynamoDB に持つことでステートレスにしつつ、条件付き書き込みでアトミック性も保証します。
ロック処理の仕組みを作る
まずは DynamoDB にシンプルなテーブルを作ります。テーブル名は lambda_lock_manager
、プライマリーキーは key
としました。
Python と Boto 3 を使ってロック処理を実装してみるとこんな感じです。
import boto3
import time
client = boto3.client('dynamodb')
table_name = 'lambda_lock_manager'
item_name = 'lock_test'
def lock(table, item):
try:
client.put_item(
TableName = table,
Item = {
'key': {'S': item}
},
Expected = {
'key': {'Exists': False}
}
)
return True
except Exception, e:
return False
def unlock(table, item):
client.delete_item(
TableName = table,
Key = {
'key': {'S': item}
}
)
def lambda_handler(event, context):
result = lock(table_name, item_name)
print result
if not result:
print "Locked. Nothing to do."
return
time.sleep(15)
unlock(table_name, item_name)
ロックを取って、15 秒間スリープして、ロックを解除するという単純なスクリプトです。
ロックの詳細は lambda_lock_manager
テーブルに lock_test
というキーを Put して、
- すでに存在していたら、他の誰かがロック済み。例外を投げて
False
を返す - 存在していなければ、誰もロックしていない。キーを Put して
True
を返す
という仕組みになっています。
テストしてみる
この Lambda 関数に S3 の Put イベントトリガーを設定して、同時に複数ファイルをアップロードしてみます。
START RequestId: 3dcfa1a0-532d-11e6-9605-xxxxxxxxxxxx Version: $LATEST
True
END RequestId: 3dcfa1a0-532d-11e6-9605-xxxxxxxxxxxx
REPORT RequestId: 3dcfa1a0-532d-11e6-9605-xxxxxxxxxxxx Duration: 15315.15 ms Billed Duration: 15400 ms Memory Size: 128 MB Max Memory Used: 34 MB
CloudWatch Logs のログを確認すると、True
が出力されているのは常に 1 つです。実行時間も約 15 秒です。
START RequestId: 3df2e1a6-532d-11e6-85b1-xxxxxxxxxxxx Version: $LATEST
False
Locked. Nothing to do.
END RequestId: 3df2e1a6-532d-11e6-85b1-xxxxxxxxxxxx
REPORT RequestId: 3df2e1a6-532d-11e6-85b1-xxxxxxxxxxxx Duration: 427.45 ms Billed Duration: 500 ms Memory Size: 128 MB Max Memory Used: 34 MB
他は False
が出力されており、正しくロックされていることが確認できました。
まとめ
DynamoDB の条件付き書き込みを使えば、Lambda でもアトミックなロック処理が実装できます。これで活用の幅がまたひとつ広がりそうです。