Shogo's Blog

Apr 11, 2026 - 2 minute read - go golang aws

Amazon S3で分散ロックを実現するsets3lock

背景・目的

先週は三日三晩寝込んでしまい非常につらい思いをしたいっちーです。

さて、もう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版です。

使い方は 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

参考