2026年8月22日、AWS Community Day Singaporeで登壇してきた。
演題は
「Distributed Transactions Under Fire: Building a Zero-Oversell Flash Sale Platform with Amazon Aurora DSQL」
最初はもっと短かったが、結果長くなった
日本語にすると
「分散トランザクションの試練:Amazon Aurora DSQLでオーバーセルゼロのフラッシュセール基盤を作る」
といったところだ。
会場に入って最初に気づいたのは、聴衆が11人しかいなかったことだった。
今まで登壇した中で一番少ない。同じ時間帯に別会場でKiroのワークショップをやっていて、そちらに人が流れていたようだ。
人数が少ないと逆に緊張するもので、質疑応答もゼロだった。
以下、話した内容をそのまま記事にする。
30秒で在庫が尽きる世界
限定スニーカーやライブチケットのフラッシュセールは、だいたい30秒で在庫が尽きる。
数千人が数十個の商品に殺到する世界だ。
こういうワークロードで従来型のDB構成が壊れ方は大きく2つある。
ひとつは、数千の同時リクエストが同じ行を叩きにいってレースコンディションが起き、注文が重複したり失敗したりする「DBがボトルネックになる」パターン。
もうひとつは、スパイクに耐えるためにピーク容量で常時起動しておくしかなく、セール自体は30秒しか続かないのに24時間分の料金を払い続ける「過剰プロビジョニングで無駄が出る」パターンだ。
Amazon Aurora DSQLとOCC
この2つの問題に対して用意した答えがAmazon Aurora DSQLだった。
サーバーレスで分散されていて、強整合性を持つSQLエンジンという触れ込みで、インスタンスも容量計画も要らず、セールとセールの間はゼロまでスケールダウンする。
読み取りは常に最新で、split-brainも起きない。
肝になるのは行ロックを使わない楽観的並行性制御(OCC、Optimistic Concurrency Control)だ。
行単位でロックを取る代わりに、コミットのタイミングで競合を検出する。
買い手Aと買い手Bが同じ商品にほぼ同時にアクセスした場合を考えると分かりやすい。
両者ともBEGINでトランザクションを開始し、在庫を読んで1以上あることを確認し、注文レコードをINSERTする。
ここまでは両方とも成功しているように見える。
だが実際にCOMMITする段階で、片方はSUCCESSになり、もう片方はOCC ERRORで弾かれる。弾かれた側はリトライして「sold out」を返す。
分散ロックも合意プロトコルも要らず、コミット時点で確定的にオーバーセルゼロを実現できる。
DROPZEROというデモアプリ
このOCCが実際に機能することを証明するために、DROPZEROという限定ドロップ型のフラッシュセールストアを作った。
構成はシンプルで、Vercel上のNext.jsがREST API経由でAWS Lambdaを呼び、LambdaがSQL/IAM接続でAmazon Aurora DSQLにアクセスする。冪等性チェックだけAmazon DynamoDBに任せている。DSQLはコネクションプールを自前で管理しなくていいので、Lambda側にプーリングの仕組みを持たせる必要がない。
購入トランザクションの中身は、
①BEGINでトランザクションを開始し
②在庫をSELECTし
③在庫が0ならROLLBACK
④そうでなければ注文をINSERTし
⑤最後にCOMMITする
この5ステップだけだ。
COMMITのタイミングでOCCが競合の有無を検証してくれる。
本番で実際に壊れた話
ここが今回一番話したかった部分で、綺麗に動いた話よりも壊れた話のほうが実は多い。
ひとつ目は接続モデルの問題で、IAMトークンが15分で失効するのに、Lambdaがトークンを更新せずにコネクションを使い回していたことに気づかなかった。
しかも認証失敗は同時アクセスが増えたときだけ表面化するので、開発中は全く気づけなかった。修正は単純で、実行コンテキストごとにトークンを取り直すようにした。
コールドスタートのたびに generate-db-auth-token を呼び、DSQLの接続をリクエストをまたいでキャッシュしないようにする。
ふたつ目はリトライストームだ。
100人の買い手のうち約90人がOCCエラーでリトライすることになるので、何も考えずにリトライさせると3波目あたりで負荷が3倍に膨れ上がる。
これは指数バックオフとジッターで対処した。基本ディレイ100msから始めて各リトライで倍にし、±50%のジッターでリトライが同時に重ならないようにする。最大3回リトライしたら、それ以上は粘らずに正直に「sold out」を返すサーキットブレーカーも入れた。正確性と引き換えに、コールドスタート時とリトライ時のレイテンシは増える。このトレードオフは受け入れることにした。
デモ本番では、100人の同時買い手が10個の在庫に殺到するシナリオを流した。
結果は10/10件が正しくコミットされ、残り90件はクリーンな「sold out」応答になり、オーバーセルは0件だった。DSQLが持ちこたえてくれた。
DSQLを使うべき場面、避けるべき場面
フラッシュセールやチケットドロップのようにバースト型のワークロードで、リージョンをまたいだ強整合性が必要で、運用チームを持たずサーバーレス前提でアーキテクチャを組みたいなら、DSQLは良い選択になる。
一方で、集計やフルテーブルスキャンを多用する重い分析はAmazon RedshiftやAmazon Athenaに任せたほうがいいし、多段の複雑なJOINを前提にした深いリレーショナルスキーマにも向かない。
ストアドプロシージャやトリガーは今のところサポートされていないのでアプリケーション層にロジックを寄せる必要がある。
常時高スループットが続くOLTPなら、素直にプロビジョニング型インスタンスのほうが安く済む。
また、コストの形も面白い。
サーバーレスDBはドロップの瞬間だけコストが跳ね上がり、それ以外はほぼゼロに張り付く。
常時起動のプロビジョニング型は平坦な線を描き続ける。
30秒しか続かないイベントのために24時間分払うか、使った分だけ払うかの違いがそのままグラフの形に出る。
持ち帰ってほしい3つのこと
まとめると
①OCCは魔法ではなく最初からリトライ前提で設計するものであること
②サーバーレスDBはコストモデルそのものを変えること
③冪等性キーは後付けできるものではなく最初から組み込むべきものであること
の3つを話した。
11人と質疑応答ゼロについて
正直、Kiroワークショップに客を持っていかれたと分かった瞬間は驚いた。
ただ、少人数だからこそ緊張したというのも本当のところで、質問が飛んでこない静けさは、それはそれで別の緊張を連れてくる。
次にシンガポール等海外で話す機会があれば、今度は裏番組に負けないネタを持っていきたい。
最後まで読んでいただきありがとうございました。
