💨

Momento 入門 その6:Momento Topics の到達性や冪等性の確認

2023/09/08に公開

Momento 入門 その5:Momento Topicsを触ってみるではPub Sub メッセージングサービスのMomento Topicsを触ってみました。

Pub Subメッセージングサービスには、それぞれ技術特性があります。まず到達性については1) 到達を保証していないもの 2) 最低1回の到達を保障するもの 3) 1回のみの配信を保障するもの。当然1)が一番高速に動作することになります。2)の場合アプリケーション側で同じメッセージを複数回受け取ることになるので冪等性の担保も重要です。冪等性とは、「ある操作を1回行っても複数回行っても同じ処理結果になる特性」のことです。
また到達保証についても注意が必要です。メッセージ配信基盤とサブスクライバ間の通信ができない状態の場合、もしくはサブスクライバのプロセスが落ちている場合。メッセージ配信基盤からのメッセージ送出をもって保証とするケースと、サブスクライバのリトライを加味して保証とするケースがあります。これは主にデータ削除タイミングに依存します。データを送出した時点でメッセージ配信基盤がデータを消すケースとサブスクライバが読み込んだ時点でサブスクライバ側の指示でデータを消すケースです。当然前者の方が高速です。

Momento のドキュメントを読んでみる

Momento Topics は"Fire-and-forget (発火したら忘れる)"メッセージモデルなので、一度項目をパブリッシュしたら、それはその時点でそのトピックの全サブスクライバーに即座に送られ、その後捨てられます。項目の永続化や配送保証をする仕組みはもっていません。従って、Momento Topics は低レイテンシであることが重要で、たまに項目を失っても問題なく動作するようなアプリケーションに最も適しています。

と記載されています。つまり、到達は保証しておらず、サブスクライバの再送処理は行われずメッセージは即時削除される、ということです。これは最も高速にメッセージ配信を行うことを主眼にサービスが設計されているようです。
どのようなユースケースでしょうか。例えばSNSサイトの通知などがそれに当たるかもしれません。巨大なSNSサイトは数千万や数億人のユーザーが存在しそれらが書き込みを行います。その書き込みは別のユーザーにプッシュで通知がなされますが、データの整合性を重視したメッセージ配信ではとてもではないがその規模を裁くことはできません。このため多少のデータロスを許容したうえで「ほぼすべて」のユーザーに通知が届くことを重視するケース、などです。

Fan Out 型をためす

では前回の記事で使った環境を元にFan out型をテストしていきます。Fan Out 型とは扇風機が空気を拡散することからきている言葉です。一つのメッセージを複数のサブスクライバに配信する形式です。
もう1台別のEC2でサブスクライバを立てておきます。
サブスクライバを起動する前に

./momento topic publish foo bar

でメッセージを配信した後、マネージメントコンソールでキャッシュをfooで検索するとヒットしません。つまり、サブスクライバがメッセージを受け取ったかどうかにかかわらず、即時メッセージが削除されていることがわかります。

では次に2台の環境で

./momento topic subscribe foo

を起動しておくと、publish時点で同時に2台がメッセージを受信することがわかります。

到達順の確認

メッセージ基盤はメッセージの到達順を維持するものと維持しないものがあります。つまりPublisherがメッセージをパブリッシュした順にSubscriberに送り出すか?です。順番を維持するものはFIFO型といいますが、一般的に順番を維持しないもののほうが速度は速くなります。
個人的な意見としてはこのようなメッセージ基盤を使う際FIFO型にこだわることはあまり意味がないように思います。なぜなら、メッセージ基盤が受け取った順番にメッセージを送り出すことに配慮したとしても、サブスクライバまでの通信環境でメッセージが入れ替わることが考えられるためです。このためどのみち、サブスクライバでは順番がばらばらで届くことを想定した開発を行っておくことが正しいイベントドリブンアーキテクチャであるといえます。
Momentoのドキュメントには到達順の記載はありませんでしたが、スピードを重視しFIFO型ではないと思われます。

ではやってみます。コマンドは以下です。

for i in {1..50}; do
  ./momento topic publish foo bar$i
done

こちらを普通にシェルで実行します。

100などの大きい数字で普通にやると以下のエラーが出ますので、Momentoに上限緩和申請を行う必要がありますが、50だと大丈夫そうでした。

ERROR: LimitExceeded { description: "resource exhausted", source: TonicStatus(Status { code: ResourceExhausted, message: "Api rate limit exceeded. Please contact support@momentohq.com for a limit increase", metadata: MetadataMap { headers: {"content-type": "application/grpc", "date": "Fri, 08 Sep 2023 04:38:33 GMT", "access-control-expose-headers": "grpc-status,grpc-message,grpc-encoding,grpc-accept-encoding", "content-length": "0", "access-control-allow-credentials": "true", "vary": "origin", "vary": "access-control-request-method", "vary": "access-control-request-headers"} }, source: None }) }

2つのSubscriber側では両方ともbar1 から bar50まで順番に出力されていました。FIFOを保障していないとは言え、そこまで順番が入れ替わることはなさそうです。
Publisherと2つのSubscriberは全て同じ東京リージョンにあるためNetwork条件が近質なので、もう一台サンパウロリージョンでSubcriberを立ち上げましたが結果は同じでした。

とはいえ繰り返しですが、その順番に依存しない実装が必要ですので注意しましょう。

Topic の設計について

Momento TopicsではTopic = チャネルとなりあらかじめチャネルを管理者が作っておく必要がないのは大きなメリットです。ただし運用設計にはいくつか考慮が必要です。

イベントドリブンアーキテクチャを採用したシステムはビジネスの拡大に伴いシステムが成長するにつれ爆発的にモジュールが増えていきます。従来のモノリシックなシステムと異なり、個々のモジュールを小さく独立し維持することでメインテナンス性を上げていく手法ですから当然です。それにともないTopic、そして流れるメッセージも増えていきます。サブスクライバ側のほうでメッセージのフィルタリングを行えばいいのですがこれは一定の負荷が発生します。

このため最初からTopic名には例えば[システム名][モジュール名]と分割を考慮した設計が必要になります。

Discussion