We’re sorry we missed you at re:Invent, but we can still meet!

分散ロック: Momentoでより簡単に

DynamoDB Lock ClientをMomentoに置き換える。

プラティク・アガルワル
著者

Share

分散システムは魔法のようなものです。「どうしてこうなったのかわからない」というような魔法ではなく、常にそうだとは限りません。しかし、ひとたびその複雑さを理解し始めると、すべてが違って見えるようになります。数年前、オンラインでイベントのチケットを購入する際、「10分以内に購入手続きを完了すると座席を確保できます」というメッセージがよく目につきました。当時は何気なく肩をすくめたものだが、今では分散ロックのことを書いているときに、このメッセージを見て知ったような笑みを浮かべています。

分散システムでは、安定性を維持し、さまざまな障害シナリオに対処するために、さまざまなコンポーネントが効果的に協調する必要があります。この調整のための基本的なツールは、可用性が高く、ネットワーク・パーティションに強いロック・メカニズムです。例えば、何千人ものサッカーファンが同時に人気のある試合のチケットを買おうとしているとしましょう。各チケットを正しい購入者に一度だけ販売することが不可欠です。ここで、分散ロックは仮想ゲートキーパーの役割を果たし、誰かがチケットを購入するとき、その席が他の誰にも売られないようにすることができます。このメカニズムにより、秩序と公平性が保たれ、二重予約やシステム過負荷の問題を防ぐことができます。

PaxosRaftのような高度なコンセンサス・アルゴリズムは、これらの課題に対する解決策を提供するが、実装や管理が複雑です。Apache Zookeeperは、このような複雑なプロトコルを実装するための課題を抽象化しているが、その代わりにZookeeperクラスタをスピンアップしてクラスタを管理するために、もう1つの複雑なレイヤーを導入しています。しかし、Zookeeperはそれ自体価値のある強力なメカニズムであり、ロックやリースの要件には必ずしも最適ではありません。

このような課題に対して、AmazonはDynamoDBの堅牢なインフラを利用して分散ロックの実装を簡素化するライブラリ、DynamoDB Lock Clientを開発しました。このライブラリは、Zookeeperのようなシステムのセットアップやコンセンサスアルゴリズムの実装の複雑さを回避し、分散ロックを管理するためのより簡単なアプローチを提供します。私たちMomentoは、DynamoDBのライブラリの効率性とシンプルさにインスパイアされ、同じ原則を持ちながら、私たちのシステムとシームレスに統合できるように調整され、アプリケーションのワークロードに応じて50〜90%安価ドロップインの代替品を作りました。既存のJavaコードの数行を置き換えるだけで、Momentoの使用に切り替えることができます!

分散ロックの必要性

サッカーのチケットの例では、二重予約を防ぐだけでなく、シームレスで満足度の高いユーザー体験を提供することが重要です。ファンが座席を選択する際、支払いの詳細を入力したり、駐車場やグッズなどの追加オプションを選択したりと、購入完了までにいくつかのステップを踏む必要があるかもしれません。このプロセスを通じて、ファンにとっては、自分が選択した座席が取引の最後まで自分のために確保されているという確信を持つことが非常に重要です。そこで、Momentoのロックライブラリーが重要な役割を果たします。

ファンが座席を選択すると、Momento Lock Clientは即座にその座席をロックします。このロックは、トランザクションの様々なステップを通じて、ファンが最初に選択した座席を失わないようにします。Momentoをロックニーズに統合する方法を見てみましょう!

既存のDynamoDBロッククライアントライブラリを変更する場合は、クライアントの初期化を変更するだけです。また、Momentoのロックライブラリをアプリケーションに直接統合したい場合は、もう少しお待ちください!

依存先を追加する:


<dependency>
  <groupId>software.momento.java</groupId>
  <artifactId>momento-dynamodb-lock-client</artifactId>
  <version>0.1.1</version>
</dependency>

クライアントを初期化:


final String hostname = InetAddress.getLocalHost().getHostName();
final String cacheName = "ticket-lock";
final CredentialProvider credentials = CredentialProvider.fromEnvVar("MOMENTO_API_KEY");
        
        final AmazonDynamoDBLockClient momentoLockClient = new MomentoDynamoDBLockClient(MomentoDynamoDBLockClientOptions.builder(cacheName)
                .withConfiguration(Configurations.Laptop.latest())
                .withCredentialProvider(credentials)
                .withLeaseDuration(600L)
                .withHeartbeatPeriod(3L)
                // host which acquires lock is the owner
                .withOwner(hostname)
                // let Momento manage heartbeating!
                .withCreateHeartbeatBackgroundThread(true)
                .withTimeUnit(TimeUnit.SECONDS)
                .build());

上記のコードには ticket-lock という Momento キャッシュと、環境変数 MOMENTO_API_KEY で設定した API キーが必要です。どちらも Momentoコンソール から作成できます。

では、座席の予約に移りましょう:


// Seat ID for the ticket being purchased
final String seatID = "A5"; 

// Attempt to acquire a lock on the seatID
final Optional lockItem =
        momentoLockClient.tryAcquireLock(AcquireLockOptions.builder(seatID).build());
if (lockItem.isPresent()) {
    System.out.println("Success! Seat " + seatID + " is reserved for you. Complete your purchase within the next 10 minutes.");
    
    // Code to guide the user through the remaining purchase steps goes here

    momentoLockClient.releaseLock(lockItem.get());
    System.out.println("Thank you for your purchase! Seat " + seatID + " is now officially yours.");
} else {
    // case where more than 1 request comes from the front-end for the 
    // same seat at the same time
    System.out.println("Apologies, seat " + seatID + " seems to be in another transaction. Please try selecting a different seat.");
}

// Closing the client stops heartbeating, so this should be done at the end of the application lifecycle
momentoLockClient.close();

クライアントの初期化において、ハートビートの概念は、人間の生活におけるハートビートの基本的な重要性を反映し、重要な役割を果たします。分散システムでは、ロックの完全性と機能を維持するために、ロックに対するハートビートが重要です。このようなシステムへの定期的なシグナルは、心臓の鼓動が生命力を示すように、ロックを保持するクライアントがまだ動作可能であることを確認します。

サッカーのチケット購入の例で言えば、ハートビートによって、チケット購入プロセスにおけるファンのスムーズなユーザー体験が保証されます。ファンが座席の選択から支払いの詳細の入力まで、様々なステップを進むにつれて、ロックのハートビートが選択した座席の権利を維持します。この一貫したハートビートがなければ、システムは座席のロックを早々に解除してしまう可能性があり、ファンにとってフラストレーションのたまる体験につながる可能性があります。このハートビートプロセスは、Momentoのライブラリによってシームレスに処理され、トランザクション全体を通してロックの中断のない効率的な管理を保証します。

しかし、なぜ悲観的にロックするのか?

一つの根本的な疑問が生じます: なぜ、同じ予約システムで楽観的同時実行制御(OCC)を使う代わりに、悲観的に座席をロックする必要があるのでしょうか?

OCCは、データの衝突はめったに起きないという前提で運営されています、チケット販売のようなトラフィックの多いシナリオでは、この前提は破綻します。ファンが座席を選択した瞬間、別のファンが同じ座席を狙う可能性は高いといえます。このようなシナリオでOCCを採用すると、ユーザーにとってフラストレーションのたまる体験になりかねません。ファンが予約の最終段階まで来て、その席が他の人に取られていることに気づくかもしれません。このような後期の紛争解決は、ユーザーの信頼と満足度を損なう可能性があります。したがって、リソースの集中にもかかわらず、悲観的なロッキングは私たちのチケット販売システムにとって戦略的な選択であり、信頼性とシームレスな予約体験を毎回すべてのファンに保証するものなのです。

しかし、なぜMomentoなのか?

ロック管理にMomentoを選択することで、このようなシステム特有の要件に合わせた明確な利点が得られます:

費用対効果: Momentoのソリューションは、特に規模が大きくなるほど、コスト効率が大幅に向上します。この手頃な価格は、大量のトランザクションを処理するシステムにとって非常に重要です。DynamoDBとMomento Lockクライアントの詳細なコスト分析をご覧ください。
高トラフィックとパフォーマンスに最適化: Momentoは、高トラフィックとバーストシナリオ用に設計されており、多数の同時リクエストを効率的に処理し、スムーズなユーザーエクスペリエンスを保証します。
強力な一貫性と耐久性が必要ですか?:ロックは一般的に短命から中命であることを考えると、DynamoDBが提供する特性はそれほど重要ではなくなります。Momentoのシステムは可用性が高いため、ロックの紛失や二重発行は最小限に抑えられます。
インフラ管理不要: Momentoのロック・ライブラリのセットアップと管理が簡単なため、技術的なオーバーヘッドが大幅に削減され、時間とリソースを節約できます。

まとめ

Momentoのロック・ライブラリは、トラフィックの多いロック・シナリオを簡単かつ効率的に管理するための、シームレスでコスト効率の高いソリューションを提供します。分散ロック管理に堅牢でユーザーフレンドリーなアプローチを求める方に理想的な選択肢です。

MomentoのDynamoDBロックライブラリの代替ドロップインを直接体験し、今すぐシステムに統合してください。

Momentoのジェネリック・ライブラリーは近日公開予定です!

Share