背景・目的
先週は三日三晩寝込んでしまい非常につらい思いをしたいっちーです。
さて、もう2年近く前になりますが、Amazon S3 が条件付き書き込みに対応しましたね。 昨年9月には条件付き削除にも対応しています。
これの何が嬉しいかと言うと、今までDynamoDBが必要だった場面で、S3だけでカバーできるケースが増えます。
他所の例だと Terraform が有名ですね。 Terraform の S3 backend は多重実行を防ぐために元々は DynamoDB が必要でした。 条件付き書き込みがリリースされてからは多重実行防止の仕組みがS3だけで動くようになりました。 構成がシンプルになって管理するリソースが減るので嬉しいですね。
僕も似たような用途で DynamoDB を利用していました。 昔 AWS Lambda と S3 を使ってyumレポジトリーを作ったときです。 yumレポジトリーのメタデータ更新が並列して走るとロストアップデートの可能性があるので、多重実行防止にDynamoDBを利用していました。
- 排他制御
- メタデータ更新が並列して走るとロストアップデートの可能性がある
- DynamoDB を使った排他的ロックを実装
「今ならDynamoDBなしで実現できるのでは!」というわけで、やってみました。 また、その副産物として sets3lock というものができたので紹介します。
sets3lock
The setlock program という排他制御を行う有名なプログラムがあります。
有志たちによって DynamoDB版やRedis版が書かれてきましたが、sets3lock はそのS3版です。
- go-setlock - Go port of setlock
- Redisを使って排他制御するwrapperコマンド Redis-Setlock をPerlとGoで書いた
- DynamoDBで分散ロックを実現するsetddblockと現代版アクセスカウンター
使い方は setlock とほぼ一緒です。
ロックにファイルではなくS3オブジェクトを指定する点だけがことなります。
たとえば以下のように指定すると、task.sh を排他的に実行します。
sets3lock s3://bucket/key task.sh
ロックファイルはS3に作成されるので、サーバーをまたいでも使えるのが大きな特徴です。
Goのライブラリーとしても使えます。
インターフェイスは setddblock と合わせてあります。
ただし(少なくとも現時点では)heartbeatを行わないので、タイムアウト時間の調整は必要かもしれません。
import "github.com/shogo82148/sets3lock"
func main() {
// ロックオブジェクトの作成
ctx := context.Background()
l, err := sets3lock.New(ctx, "s3://bucket/key")
if err != nil {
panic(err)
}
// ロックの取得
if _, err := l.LockWithErr(ctx); err != nil {
panic(err)
}
// ロックの解放
defer func() {
if err := l.UnlockWithErr(ctx); err != nil {
panic(err)
}
}()
// 排他的に実行したい処理をここに書く。
}
仕組み
ロックオブジェクトが存在する=ロックが獲得されている、とみなします。
ロックの獲得は put-object API を呼び出しているだけです。
aws s3api put-object \
--bucket bucket \
--key key \
--if-none-match '*' \
--body lock.json
条件 --if-none-match '*' が付いているのがポイントですね。
これにより、ロックオブジェクトがすでに存在した場合、つまり他のクライアントがロックを獲得している場合はエラーになります。
その場合はしばらく待ってからリトライします。
ロックの解放は delete-object APIを呼び出します。
aws s3api delete-object \
--bucket bucket \
--key key \
--if-match 'etag'
put-object APIを叩いたときに、作成したロックオブジェクトのEtagが返ってくるので、
条件 --if-match 'etag' にそのEtagを指定します。
これにより「自分の作成したロックオブジェクトを削除する」ことを保証します。
分散コンピューティングに関する一番難しい部分をS3がやってくれるので、sets3lockのやっていることは非常に簡単ですね。
頑張れば AWS CLI + bash でも実現できそうです。(やらないけど)
自作yumレポジトリーへの組み込み
さてさて、本来の目的は自作yumレポジトリーへの組み込みなのでした。
ここまでできてしまえば組み込みは簡単ですね。
メタデータの更新処理を LockWithErr, UnlockWithErr で囲うだけです。
参考までに変更したプルリクエストへのリンクを貼っておきます。
まとめ
自作のyumレポジトリーの排他制御にS3を使用することで、DynamoDB依存を取り除きました。
その副産物として sets3lock というものができたので紹介しました。
S3のみで排他制御を行うライブラリーです。 今までDynamoDBを使っていた一部のケースを代替できます。 ぜひご利用ください。
🐰 S3のロックで夢を見て
Dynamoはもう要らぬかな
条件付き書き込みの賢さで
分散の悩みは吹っ飛ぶ
クラウドの知恵、ウサギも歓喜♪by CodeRabbit
参考
- Amazon S3 が条件付き書き込みのサポートを開始
- Amazon S3 が S3 汎用バケットでの条件付き削除をサポート
- Terraform v1.10 からは S3 Backend の State Lock に DynamoDB が必要なくなる
- The setlock program
- go-setlock - Go port of setlock
- moznion/go-setlock
- DynamoDBで分散ロックを実現するsetddblockと現代版アクセスカウンター
- mashiike/setddblock
- Redisを使って排他制御するwrapperコマンド Redis-Setlock をPerlとGoで書いた
- fujiwara/go-redis-setlock
- AWS Lambda + S3 を使ってyumレポジトリを作った
- ロック機構をDynamoDBからS3へ変更 #557