サーバーレスにおけるべき等性の実装 (メッセージ編)

サーバーレスが気になる開発者に捧ぐ「べき等性」ことはじめ 第 2 回

2021-06-02
デベロッパーのためのクラウド活用方法

福井 厚

このシリーズの第一回では、べき等性とは何かについて紹介しました。べき等性とは「ある操作を 1 回行っても複数回行っても結果が同じである」という概念で、例えば実数の乗算では 0 と 1 がべき等になります (ある数字に 0 または 1 を何回掛けても同じ結果になる)。前回の記事では注文処理の例、障害復旧時のフロー処理の例でべき等性の考え方を紹介し、べき等性を保証する実装の典型的な基本パターンとして、①入力時チェック方式、②出力時チェック方式、③トランザクション型への処理への対応について説明しました。本シリーズでは AWS のサーバーレスサービスの利用を前提に、べき等性を実現する具体的な実装について紹介します。べき等性の実現には考慮すべき点が多いため、個別のユースケースを取り上げながら、どのようにべき等性を実装するかについて、これから数回に分けて解説していきます。

第一回の記事 では、クライアントからバックエンドのサービスを利用する際に、リトライ処理によって重複したリクエストを送信する可能性があることを説明しました。例えば、商品の注文情報を注文サービスに依頼するために注文のメッセージを送信するような場合、クライアント側の処理で何らかのエラーが発生してリトライ処理が行われると、同じ注文メッセージが複数注文サービスに送信される可能性があります。

第二回の本記事では、そのような場合のべき等性の実装について取り上げます。ここでは、上記で紹介した①入力時のチェック方式として、そもそものデータの重複を避ける手段を取り上げます。送信時のリトライ処理によって発生するデータの重複をデータストアに書き込む前に排除してしまう考え方です。本記事では、このメッセージの重複排除にフォーカスし、AWS のサービスを利用してどのようにメッセージの重複排除を行うのかを説明をしたいと思います。

クライアントからメッセージを送信する時に利用可能な AWS のサービスには、Amazon Simple Notification Service (以下 Amazon SNS) と Amazon Simple Queue Service (以下 Amazon SQS) があります。本記事では、これら Amazon SNS と Amazon SQS を利用してメッセージの重複を排除する方法について紹介します。入力の重複をチェックする方法は様々ですが、自分で実装する代わりに、Amazon SNS や Amazon SQS の機能を利用することで、より簡単に重複排除を実装することができます。

ご注意

本記事で紹介する AWS サービスを起動する際には、料金がかかります。builders.flash メールメンバー特典の、クラウドレシピ向けクレジットコードプレゼントの入手をお勧めします。

*ハンズオン記事およびソースコードにおける免責事項 »

この記事のデモを無料でお試しいただけます »

毎月提供されるデベロッパー向けアップデート情報とともに、クレジットコードを受け取ることができます。 


Amazon SQS とは

Amazon SQS は、分散システムにおいて安全で耐久性があり、AWS でホストされたシンプルなキューを提供します。Amazon SQS は AWS SDK でサ ポートされている任意のプログラミング言語を使用して簡単に利用することができます。Appendix に Amazon SQS を利用するメリットと標準キューと FIFO キューの比較を記載しましたのでご確認ください。FIFO キューが提供するキューへのメッセージの重複を排除する機能を用いることでメッセージの重複を排除することができます。


Amazon SQS FIFO キューによる重複排除

FIFO キューを正しく利用するためには以下の点に注意してください。

  • FIFO キューでは個別のメッセージ単位の遅延をサポートしていません (代わりにキュー全体に適用される遅延を DelaySeconds で設定可能です)。
  • FIFO キューへ送信するメッセージにはメッセージグループ ID (MessageGroupID) を指定する必要があります。メッセージはメッセージグループ ID ごとに順序付けされます。
  • FIFO キューにメッセージを送信する前に以下を確認してください :
    • アプリケーションが同一内容のメッセージボディのメッセージを複数送信する可能性がある場合は、送信する個々のメッセージに一意なメッセージ重複排除 ID (MessageDeduplicationId) を提供するようにアプリケーションを修正します。
    • アプリケーションが一意なメッセージボディを送信可能な場合は、キューの作成時にコンテンツベースの重複排除 (ContentBasedDeduplication 属性) を有効化することができます。
    • プロデューサーはリトライ処理によって同一の重複排除 ID を複数回送信する可能性があります。Amazon SQS は正常に受け付けますが、5 分以内の同一の重複排除 ID のメッセージについては配信されません。
  • コンシューマのコードを変更する必要はありませんが、メッセージの処理に時間がかかり、可視性タイムアウトが高い値に設定されている場合は、各 ReceiveMessage の実行に受信リクエスト試行 ID (ReceiveRequestAttemptId) を追加することを検討してください。これによってネットワーク障害の場合に受信を再試行し、受信の失敗によりキューが停止することを防止します。

Amazon SQS の FIFO キューを使用することで正確に一度だけのメッセージ配信 (Exactly-Once) を行い、メッセージの重複排除を行うことができます。これによりべき等性を実現するための入力チェックを実装することができます。AWS SDK を利用することでアプリケーションのコードで Amazon SQS FIFO キューへメッセージを送信することができます。

FIFO キューにメッセージを送信するコードの例 (Python)

def send_fifo_message(sqs_resource, queue_name, message, unique_id, group_id):
    
    try:
        # Get the queue
        queue = sqs_resource.get_queue_by_name(QueueName=queue_name)
        # Send message to FIFO Queue
        response = queue.send_message(MessageBody=message, 
            MessageDeduplicationId=unique_id,
            MessageGroupId=group_id)
    except ClientError:
        logger.exception("Couldn't create message")
        raise
    else:
        return response

Amazon SNS とは ?

Amazon SNS はパブリッシャー (発行者) からサブスクライバ (購読者) (プロデューサーとコンシューマとも言われます) へメッセージ配信機能を提供するマネージドサービスです。パブリッシャーはトピックにメッセージを送信することで非同期にサブスクライバと通信します。トピックは論理的なアクセスポイントとコミュニケーションチャネルです。クライアントは SNS トピックをサブスクライブ (購読) することが可能で、パブリッシュ (発行) されたメッセージを SNS でサポートしている Amazon Kinesis Data Firehose、Amazon SQS、AWS Lambda、HTTP、E メール、モバイルプッシュ通知、モバイルテキストメッセージ (SMS) などのエンドポイントを使用して受信することができます。AppendixにAmazon SNS を利用するメリットを記載しましたのでご確認ください。


Amazon SNS FIFO トピックによる重複排除

Amazon SNS も Amazon SQS FIFO キューと同じような FIFO トピックをサポートしています。Amazon SNS で FIFO トピックを利用する場合、サブスクライバには Amazon SQS FIFO キューのみを指定することができます。Amazon SNS FIFO トピックと Amazon SQS FIFO キューを組み合わせることで、厳密なメッセージの順序性と重複排除の機能を提供します。以下の図のように Amazon SNS の FIFO トピックに送信されたメッセージは、Amazon SQS FIFO キューに重複を排除しメッセージグループごとに順序性を保持して配信されます。

Amazon SNS の FIFO トピックと Amazon SQS の FIFO キューを使用することで正確に一度だけのメッセージ配信 (Exactly-Once) を行い、メッセージの重複排除を行うことができます。これにより、パブリッシュ、サブスクライブパターンでも、べき等性を実現するための入力チェック方式を実装することができます。AWS SDK を利用することでアプリケーションのコードで Amazon SNS FIFO トピックへメッセージを発行することができます。

FIFO トピックにメッセージを発行するコードの例(Python)

def publish_text_message(sns_client, phone_number, message, unique_id, group_id):

    try:
        # publish FIFO Topic message
        response = sns_client.publish(
            PhoneNumber=phone_number, 
            Message=message,
            MessageDeduplicationId=unique_id,
            unique_id=group_id
            )
    except ClientError:
        logger.exception("Couldn't publish message.")
        raise
    else:
        return response

べき等性を実現するためには、さらに考慮すべきことが

さて、ここまでの解説で、べき等性を実現するために重複排除を利用する方法として Amazon SQS の FIFO キューが利用できることを紹介しました。メッセージのファンアウトを行いたい場合は、Amazon SNS の FIFO トピックと Amazon SQS の FIFO キューを組み合わせて利用することもできます。

では、これだけで、べき等性を担保できるでしょうか。例えば、Amazon SNS FIFO トピックから Amazon SQS FIFO キューにメッセージを配信した後に、Amazon SQS FIFO キューのメッセージを AWS Lambda で取り出して Amazon DynamoDB へ保存するようなシステムを構築するとします。この場合、Lambda 関数は Amazon SQS の FIFO キューからメッセージを取り出して DynamoDB にデータを書き込みを行い、関数が正常に終了するまでの間に、何らかの理由でエラーが発生し処理が失敗する可能性があります

Lambda 関数が正常に終了しない場合、SQS のキューにあるメッセージは削除されず、可視性タイムアウトの期限が切れると再度同じメッセージが Lambda 関数によって処理されます。この時、Lambda 関数の処理で DynamoDB への書き込み自体は成功していた場合、本来 1 回のみ処理されなければならないメッセージが複数回処理されてしまう可能性があります。

次回はこの問題への対応についての解説を行います。どうかお楽しみに。


Appendix :

Amazon SQS を利用するメリット

Amazon SQSを利用することが以下のようなメリットを享受できます。

  • セキュリティ : 誰が Amazon SQS キューへ送信、受信することができるかを管理できます。
  • サーバーサイド暗号化 : AWS Key Management Service (AWS KMS) で管理された キーを使用してキューのメッセージの内容を保護するため重要なデータを送信することができます。
  • 耐久性 : メッセージの安全性を確実にするため、Amazon SQS は複数のサーバーにメッセージを保存し ます。標準キューでは、少なくとも 1 回のメッセージ配信 (At-Least-Once) をサポート、FIFO キューでは正確に 1 回だけメッセージを配信 (Exactly-Once) をサポートします。
  • 可用性 : Amazon SQS は冗長化されたインフラストラクチャを使用してメッセージへのアクセスに対する高い同時実行性と高い可用性を提供します。
  • スケーラビリティ : Amazon SQS はプロビジョニングの操作なしに透過的にスケールして負荷の増大やスパイクに対応できます。

Amazon SQS の標準キューと FIFO キューの比較

標準キュー FIFOキュー
無制限のスループット : 標準キューは、API アクションごとに 1 秒あたり、ほぼ無制限の数の API コールをサポートします。(SendMessage, ReceiveMessage, DeleteMessage)

At-Least-Once Delivery : メッセージは少なくとも一度配信されます。場合によっては 1 回以上のメッセージのコピーが配信されることがあります。

ベストエフォートな順序づけ : 場合によってメッセージは送信された順序とは異なる順序で配信されることがあります。
高スループット : バッチを利用することで、FIFO キューは API メソッド (SendMEssageBatch、ReceiveMessage、DeleteMessageBatch) ごとに秒間 3,000 トランザクションをサポートします。3,000 トランザクションは 300 API コールがそれぞれ 10 バッチのメッセージを処理することを表します。バッチを利用しない場合は、FIFO キューは API コール (SendMessage、ReceiveMessage、DeleteMEssage) ごとに秒間 300 API コールまでサポートします。(2021 年 5 月現在)

Exactly-Once-Processing : メッセージは一度だけ配信され、コンシューマが処理して削除するまで処理可能な状態で留まります。キューへの重複は取り込みません。

FIFOの配信 : メッセージは送信された順序通り受信されるように厳密に保持されます。
スループットが重要な場合のアプリケーション間のデータ送信。 イベントの順序性が重要な場合のアプリケーション間のデータ送信。

Amazon SNS を利用するメリット

Amazon SNS を利用することで以下のようなメリットを享受できます。

  • アプリケーションからアプリケーションへのメッセージング : 
    Amazon Kinesis Data Firehose のストリーミング配信、Lambda 関数、Amazon SQS キュー、HTTP/S エンドポイント、AWS Event Fork パイプラインなどのサブスクライバをサポートします。
  • アプリケーションから人への通知 :
    モバイルアプリケーション、モバイル電話番号、Eメールアドレスなどのサブスクライバへの通知機能を提供します。
  • 標準および FIFO トピック :
    FIFO トピックを利用することで、メッセージグループごとの厳密なメッセージを順序付けを可能とし、メッセージの重複を排除します。順序性が重要でない場合は標準トピックを利用することですべてのサブスクライバタイプを利用可能になります。
  • メッセージフィルタリング :
    デフォルトでは、個々のサブスクライバはトピックに送信されたすべてのメッセージを受信します。メッセージのサブセットを受信するには、サブスクライバはトピックの購読にフィルタポリシーを設定します。送信されたメッセージの属性がフィルターポリシー属性にマッチする場合、メッセージは購読しているエンドポイントに配信され、それ以外のメッセージはフィルタされます。
  • メッセージのセキュリティ :
    AWS KMS の暗号キーを利用したサーバーサイドの暗号化によって Amazon SNS のトピックに保存されるメッセージのコンテンツは保護されます。Amazon SNS と VPC のプライベートな接続を確立することも可能です。

builders.flash メールメンバーへ登録することで
AWS のベストプラクティスを毎月無料でお試しいただけます

筆者プロフィール

福井 厚
アマゾン ウェブ サービス ジャパン合同会社
シニアソリューションアーキテクト サーバーレススペシャリスト

2015 年からアマゾンウェブサービスジャパンでソリューションアーキテクトとして活動。サーバーレススペシャリストとして日々モダンアプリケーション開発とサーバーレスの活用の技術支援を行なっています。

AWS を無料でお試しいただけます

AWS 無料利用枠の詳細はこちら ≫
5 ステップでアカウント作成できます
無料サインアップ ≫
ご不明な点がおありですか?
日本担当チームへ相談する

いかがでしたでしょうか。本記事では AWS Lambda Powertools Python の概要、インストール方法、提供されるユーティリティー群について簡単に紹介しました。次回以降では、個々のユーティリティーについてより詳しく紹介する予定です。どうぞお楽しみに。