Amazon SNSの1MiB対応で変わるメッセージ設計――S3退避・分割配信からの判断基準

クラウド

Amazon SNSのメッセージ上限が256KiBから1MiBへ

Amazon SNSの最大メッセージサイズが、従来の256KiBから1MiBへ4倍に拡大されました。SNS StandardトピックとSNS FIFOトピックで、新しいMaximumMessageSize属性を設定することで利用できます。

対象となるサブスクリプションは、Amazon SQS、Amazon Data Firehose、AWS Lambdaです。MaximumMessageSizeを256KiBより大きく設定したトピックでは、サブスクリプション数が1トピックあたり最大100件に制限されます。

この変更は、単に送信できる文字数が増えたという話ではありません。これまでS3にデータを保存してSNSには参照先だけを渡していたシステムや、ペイロードを分割して複数メッセージにしていたシステムでは、メッセージング設計そのものを見直せる可能性があります。

何が変わるのか

従来、SNSで大きなイベントを配信する場合は、主に次のような方法が使われていました。

  • ペイロードをS3などへ保存し、SNSにはオブジェクトキーやURLだけを送る
  • メッセージを複数のチャンクに分割して送信する
  • データ量の大きい項目を削り、受信側が別のAPIで取得する
  • SNSではなく、専用のストレージやストリーミング基盤を組み合わせる

1MiB対応により、イベントのメタデータと業務データを1つのメッセージにまとめられるケースが増えます。例えば、生成AI処理の中間結果、IoTデバイスからのまとまった計測データ、複数の明細を含む業務イベントなどです。

ただし、1MiBまで送れることと、常に大きなメッセージを送るべきことは別です。メッセージサイズを大きくすると、コスト、レイテンシ、再試行時の転送量、受信側のメモリ使用量にも影響します。

SNSに直接載せる方式とS3参照方式

設計判断では、次の2方式を比較すると整理しやすくなります。

方式 メリット 注意点
SNSにペイロードを直接格納 受信側が追加のストレージアクセスを行わずに処理できる。イベントの一貫性を保ちやすい メッセージ転送量、再試行時の負荷、受信側の処理時間が増える
S3に保存してSNSには参照情報を格納 大きなデータを効率よく保存でき、再利用や長期保管に向く 受信側にS3アクセスが必要。削除タイミングや権限、参照先の整合性を設計する必要がある

直接配信が向くのは、受信側がメッセージ単体で処理を完結でき、データの保持期間も短いケースです。受信者が複数いても、全員が同じデータをすぐに利用するなら、S3への個別アクセスを減らせる利点があります。

一方、データを長期間保存したい場合や、同じデータを後から何度も参照する場合は、S3参照方式が有力です。SNSをデータ本体の保管場所にせず、イベント通知の役割に集中させることで、再処理や監査にも対応しやすくなります。

S3参照方式を採用する場合は、単なるURLではなく、次のような情報をイベントに含めると運用しやすくなります。

  • バケット名または論理的な保存先
  • オブジェクトキー
  • オブジェクトのバージョン情報
  • データ形式とスキーマバージョン
  • 作成日時やイベントID
  • 参照期限や削除方針

トピック属性の設定例

AWS CLIでは、トピックのMaximumMessageSize属性を設定します。例えば、SNSトピックの上限を1MiBにする場合は次のように指定します。

aws sns set-topic-attributes \
  --topic-arn arn:aws:sns:ap-northeast-1:123456789012:order-events \
  --attribute-name MaximumMessageSize \
  --attribute-value 1048576

1MiBをバイト数で指定するため、値は1048576です。トピック単位の設定であるため、同じAWSアカウント内でも、トピックごとにメッセージサイズの方針を分けられます。

大きなペイロードを利用するトピックでは、サブスクリプション数の上限が最大100件になる点も重要です。多数の購読先を必要とする設計では、トピックを分割する、別の配信階層を設ける、あるいは小さいメッセージを扱うトピックと分けるといった構成を検討します。

Spring Bootから送信する例

AWS SDK for Java 2.xを利用するSpring Bootアプリケーションでは、SnsClientのPublishRequestにメッセージを設定します。メッセージサイズの上限設定は通常、アプリケーションの起動処理やTerraformなどのIaCで管理し、送信処理と分離します。

import software.amazon.awssdk.services.sns.SnsClient;
import software.amazon.awssdk.services.sns.model.PublishRequest;

public class EventPublisher {
    private final SnsClient snsClient;
    private final String topicArn;

    public EventPublisher(SnsClient snsClient, String topicArn) {
        this.snsClient = snsClient;
        this.topicArn = topicArn;
    }

    public String publish(String eventJson, String eventGroupId) {
        PublishRequest request = PublishRequest.builder()
                .topicArn(topicArn)
                .message(eventJson)
                .messageGroupId(eventGroupId)
                .build();

        return snsClient.publish(request).messageId();
    }
}

messageGroupIdはFIFOトピックを利用する場合の例です。Standardトピックでは不要です。実際のアプリケーションでは、送信前にJSONのバイト数を計測し、想定する上限を超えた場合にS3参照方式へ切り替える、または送信を拒否する処理を入れると安全です。

import java.nio.charset.StandardCharsets;

public class PayloadValidator {
    private static final int MAX_BYTES = 1024 * 1024;

    public static void validate(String payload) {
        int size = payload.getBytes(StandardCharsets.UTF_8).length;
        if (size > MAX_BYTES) {
            throw new IllegalArgumentException(
                    "SNS payload exceeds 1 MiB: " + size + " bytes");
        }
    }
}

文字数ではなくUTF-8でエンコードしたバイト数を確認するのがポイントです。日本語などのマルチバイト文字を含むJSONでは、文字数とバイト数が一致しません。また、実運用では余裕を持ったアプリケーション上限を設け、イベントの共通メタデータや形式変更によって上限を超えないようにします。

SQS連携で注意すべきこと

SNSからSQSへ大きなメッセージを配信する場合、受信側ではメッセージ本体の取得、JSONデシリアライズ、業務処理のすべてが重くなります。特に次の点を確認する必要があります。

  • SQSコンシューマーのメモリ使用量
  • 1回のポーリングで取得するメッセージ数
  • メッセージ処理時間と可視性タイムアウト
  • 失敗時の再試行回数とDLQへの移行条件
  • ログやトレースにペイロード全体を出力していないか

処理に時間がかかるイベントでは、可視性タイムアウトが短すぎると、処理中に同じメッセージが再び取得される可能性があります。大きなメッセージを導入する際は、平均値ではなく、デシリアライズや外部API呼び出しを含めた最大処理時間を基準に、再試行設計を見直します。

また、同じ大きなメッセージが再配信されると、そのたびに受信処理とネットワーク転送が発生します。冪等性キーをイベントに含め、受信側で重複処理を防止することが重要です。

Lambda連携で注意すべきこと

SNSからLambdaへ配信する場合は、関数のメモリサイズだけでなく、ペイロードを処理するコードの特性も確認します。JSONをオブジェクトへ展開すると、文字列そのものより大きなメモリを使用することがあります。

例えば、次のような実装は避けるべきです。

  • 受信した大きなJSONを何度も文字列化する
  • ペイロード全体をINFOログへ出力する
  • 1イベント内の配列を無制限に展開する
  • 受信後に同じ内容を複数の一時オブジェクトへコピーする

Lambdaの再試行によって同じイベントが複数回処理される可能性もあるため、データベース更新や外部API呼び出しでは冪等性を確保します。大きなイベントを同期的に処理するより、SQSを間に置いて処理速度を平準化する構成が適する場合もあります。

コストとレイテンシの考え方

メッセージサイズが大きくなると、1回の発行で扱うデータ量が増えます。実際の費用は利用するAWSサービス、リクエスト数、リージョン、転送先などに依存しますが、少なくとも次の観点で試算すべきです。

  • SNSから各サブスクリプションへの配信量
  • SQSでの受信、削除、再試行に伴う処理量
  • Lambdaの実行時間とメモリ設定
  • S3へ退避する場合の保存、取得、ライフサイクル管理
  • 失敗時に同じペイロードが再送される回数

購読先が複数ある場合、同じペイロードが各購読先へ配信されます。1MiBに近いイベントを多数の購読先へ送る設計では、購読先数が増えるほど転送量と処理負荷も増加します。

一方、S3参照方式ではSNSのメッセージ自体は小さくできますが、受信者ごとにS3からデータを取得する構成になると、アクセス数とレイテンシが増える可能性があります。すべての購読者が同じデータを必ず読むのか、必要な購読者だけが読むのかによって、適した方式は変わります。

1MiBを使うべきケース、使わないケース

直接ペイロードが有力なケース

  • 受信側がイベント単体で処理を完結できる
  • データの保持期間が短い
  • 受信者が追加のS3アクセスを行うとレイテンシが問題になる
  • 分割メッセージの順序管理や再構成を避けたい
  • 1つのイベントとしてアトミックに扱いたい

S3参照方式が有力なケース

  • 大きなデータを長期保存したい
  • 後から同じデータを再処理したい
  • 購読者によって必要なデータ量が異なる
  • ペイロードが1MiBを超える可能性がある
  • SNSを通知とルーティングに集中させたい

メッセージ分割を慎重に扱う理由

メッセージ分割は、上限を回避する手段ではありますが、受信側に再構成処理が必要です。チャンクの欠落、重複、順序逆転、タイムアウトなどを考慮しなければならず、障害対応も複雑になります。

分割を採用する場合は、次の情報を各チャンクに含めます。

  • 元イベントの一意なID
  • チャンク番号
  • 総チャンク数
  • スキーマバージョン
  • 冪等性を判定するための識別子

ただし、単に大きなJSONを分割するだけでなく、データ本体をS3などに保存して参照イベントを送る方が、再試行や再処理を設計しやすいケースもあります。

導入時のチェックリスト

1MiB対応を既存システムへ導入する際は、次の項目を段階的に確認します。

  • MaximumMessageSizeを設定したトピックのサブスクリプション数が100件以内か
  • SNS、SQS、Lambda、Data Firehoseの接続構成が要件に合っているか
  • UTF-8換算の最大ペイロードサイズをテストしているか
  • SQSの可視性タイムアウトとDLQ設計を見直したか
  • Lambdaのメモリ、タイムアウト、同時実行数を検証したか
  • 失敗時の再送によるコストと処理負荷を試算したか
  • ログやトレースに機密情報・大容量データを出力していないか
  • 受信側で重複処理を防止できるか
  • S3参照方式から移行する場合、既存コンシューマーとの互換性を保てるか

まとめ

Amazon SNSの1MiB対応は、S3への退避やメッセージ分割を減らせる実用的な拡張です。特に、SQS、Lambda、Data Firehoseと組み合わせたイベント連携では、イベント単位で必要なデータを運びやすくなります。

ただし、ペイロードを大きくするほど、再試行時の転送量、受信側のメモリ使用量、処理時間、ログ管理、コストに影響します。「1MiBまで載せる」ことを目的にするのではなく、イベントを直接配信するのか、S3参照方式にするのかをデータの寿命と利用形態で判断することが重要です。

まずはトピックごとに最大サイズの方針を定め、代表的な最大ペイロードで負荷試験を行います。そのうえで、処理時間、再試行、購読者数、データ保管の要件を含めて設計すれば、SNSの1MiB対応を安全にマイクロサービス基盤へ取り込めます。

出典: AWS What’s New

タイトルとURLをコピーしました