[レポート] DAT374 :[新機能!] Amazon DynamoDBのTransactionsを用いたモダンなアプリケーションの構築 #reinvent

re:Invent2018のDAT374「[NEW LAUNCH!] Building Modern Applications Using Amazon DynamoDB Transactions」のレポートです
2018.11.29

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

はじめに

サーバーレス開発部@大阪の岩田です。 この記事はre:Invent2018のDAT374 [NEW LAUNCH!] Building Modern Applications Using Amazon DynamoDB Transactionsのレポートになります。

概要

以下が公式の概要となります。

DynamoDB transactions enable developers to maintain the correctness of their data at scale by adding atomicity and isolation guarantees for multi-item conditional updates. With transactions, you can perform a batch of conditional operations, including PutItem, UpdateItem, and DeleteItem, with guarantees. Come to this session to learn how DynamoDB transactions work, the primary use cases it enables, and how to build modern applications that require transactions.

ホットな話題のDynamoDBトランザクションに関するセッションです。 DynamoDBトランザクションの主要なユースケースとトランザクションを必要とする最新アプリケーションの構築手法について学びます。

参考

[速報]待望のDynamoDBのトランザクションがリリースされました! #reinvent

DynamoDBのトランザクションについてFAQ形式で答えてみる #reinvent

スピーカー

  • Yossi Levanoni - Sr Manager, Software Dev, AWS

レポート

アジェンダ

セッションで何を学ぶか

  • トランザクションAPIがどのように動くか
  • トランザクションAPIをアプリから使うためのケーススタディとデザインパターン
  • 既存のDynamoDBの機能との相互作用について

なぜトランザクションが重要か?

  • 現実世界のトランザクションのシナリオを実行できる
  • トランザクションAPIによりアプリの実装がシンプルになる

トランザクションAPIについて

  • TransactWriteItems
    • 最大10アイテムまで
    • 条件を評価
    • 全アイテムについて条件を評価した結果がTrueならWriteされる
  • TransactReadItems
    • 最大10アイテムまで
    • 全アイテムが一貫性を持った状態で返却される

DynamoDBにおけるACID特性

  • トランザクションをサポートする前は単一アイテムへのオペレーションのみ
  • トランザクションがサポートされたことにより、複数アイテムへのオペレーションがサポートされる
  • 新しくTransactWriteItems、TransactReadItemsができるようになった
  • トランザクションの分離レベルはシリアライズ
  • BatchxxxItemsとの違い
    • BatchxxxItemsは並列で処理をする
    • 一部成功して一部失敗という状況が起こり得る。そういう異常ケースのハンドリングはアプリ側が自前実装する必要があった

トランザクションの適用範囲

  • トランザクションが適用される範囲
    • パーティションキーが異なってもOK
    • テーブルをまたがってもOK
    • 同じリージョン限定
    • 同じAWSアカウント
    • DynamoDB tablesのみ

ユースケース1 ユーザー管理

  • シナリオ
    • ユーザーの特定はユーザー名とメールアドレス(任意)で行う
    • 2人のユーザーが同じユーザー名、メールアドレスを登録するのはNG
    • ユーザーはメールアドレスを自由に設定できる

テーブル設計の例

  • ユーザーテーブルに2種類のレコードを保持
    • Profileレコード
    • Aliasレコード ユーザー名への参照を持つ
  • ID列を入力値で検索してレコードを返却
    • UserNameRefがあればさらにレコードをたどる

新規登録処理の例

  • Conditional Expression付きの2つのPut処理をTransactionWriteで実行
  • トランザクションが成功したら2レコードINSERTされる

更新処理の例

  • Conditional Expression付きの2つのPut処理と1つのDelete処理をTransactionWriteで実行

ユースケース2 ホテルの予約

  • シナリオ
    • ゲストが部屋を予約
    • ゲストがチェックインすることで予約が成立する
    • ゲストがチェックアウトすることで予約がクローズする
  • 複数アイテムの更新を一貫して実行する必要がある

データモデル

  • 3つのレコードタイプ
  • Guests
    • IDがキー
    • 予約した部屋の一覧とチェックインした部屋の一覧を保持
  • Reservations
    • IDがキー
    • GuestのIDをもつ
    • 状態を管理
  • Rooms
    • IDがキー
    • 紐付くReservationを保持
    • 状態を管理

データのイメージ

予約の流れ

  • シナリオ
    • クライアント側でユニークな予約IDを生成
    • クライアントがTransactWriteItemsリクエストを発行
    • 新しいReservationの条件付きPut
    • 対象ユーザーのReservationsに登録したReservationを追加
  • トランザクションを使うことでダブルブッキングが回避できる

Johnが予約する場合(予約直後)

  • ResavationのStateはPENDING
  • RoomのStateはFREE

Johnが予約する場合(チェックイン直後)

  • CustomerのOccupiesRoomに対象RoomのIDが追加される
  • ResavationのStateはFULFILLED
  • RoomのStateはOCCUPIED

Johnが予約する場合(チェックアウト後)

  • CustomerのOccupiesRoomから対象RoomのIDを削除
  • CustomerのReservationsから対象ReservationsのIDを削除
  • ReservationsのStateをCLOSEDに更新
  • RoomのStateをFREEに戻す
  • RoomのRentedReservationから対象ReservationsのIDを削除

トランザクションAPIのインテグレーション

並列処理の制御

  • 楽観的ロック
    • デッドロックは発生しない
    • 低レイテンシ
    • スケールアウトしやすい
  • 開発者の責務
    • スケールアウトできる設計
    • 不要なコンフリクトを避ける

トランザクションが失敗する要因

  • アイテム別の理由
    • 条件にマッチしない 
    • キャパシティユニットの不足
    • トランザクションの競合
  • 他の理由
    • トランザクション中
    • サービスのエラー
    • リクエスト過剰
    • パーミッション

トランザクションの再試行

  • クライアントリクエストトークンを利用
    • SDKを使う場合はSDKが自動でやってくれる
    • 10分間利用可能
    • トランザクションの状態と結果の情報を保持している

他のDynamoDBの機能との統合

  • Stream トコミットされた後に順次反映されます。
  • バックアップとPITR トランザクションが成功した後に順次反映
  • GSI コミットされた書き込みだけが個別にインデクシングされる
  • GlobalTable デフォルトでは無効 サポートを有効化できるが、一貫性は保証されない
  • DAX 非対応だがロードマップにある

アクセス制御

  • 新しいパーミッションが追加
    • dynamodb:ConditionCheck
  • 4つのパーミッションが必要

ユースケース3 メディアファイルの管理

  • シナリオ
    • ソーシャルメディア
    • 投稿はメディアファイルを効率的に共有する必要がある
    • 投稿は複数のメディアファイルを持てる
    • どの投稿からも参照されていないメディアファイルは削除

データモデル

  • Attachment(メディアファイル)
    • S3オブジェクトへの参照がキー
    • Postからの参照カウンタを保持
    • ライフサイクル管理様にTTLを保持
  • Post(投稿)
    • キーはID
    • Attachmentの一覧を保持

メディアファイルの管理

  • 参照カウンタでメディアファイルを参照している投稿の数を表現
  • 参照カウンタが1以上であれば属性S3RefがS3のオブジェクトへのリンクを保持
  • TTLが有効期限切れかつ参照カウンタが0なら対象レコードを自動で削除

メディアファイルの登録処理と投稿への関連付け

  • ユニークなS3キーを作成
  • レコード生成
    • 参照カウンタを0に設定
    • TTLを24時間後に設定
  • S3にオブジェクトをUP
  • TransactWriteItems
    • 既に参照されていないかチェック
    • 参照カウンタに+1とTTLを削除
    • 投稿のAtachment一覧を更新

既存メディアファイルを別の投稿に関連付ける処理

  • 既存の投稿が参照しているメディアファイルのIDをコピー
  • 以後は登録処理と同様

Thank You!!

まとめ

トランザクションを活用すべきユースケースについてテーブル設計と共に分かりやすく解説してくれました。 DynamoDBのトランザクションは分離レベルがシリアライズで固定なので、RDBのトランザクションと全く同じ感覚で使うことはできません。 いわゆる「アンチパターン」な設計をしないように、本セッションの内容を参考にしながらシステム設計を考えていきたいと思います。