0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

SNSからSQSへ必要なイベントだけ届ける:FilterPolicyの実装とハマりどころ

0
Last updated at Posted at 2026-10-27

SNS(Amazon Simple Notification Service)は、あるイベントを複数の購読先へ配信する仕組みです。SQS(Amazon Simple Queue Service)は、受け取った仕事をキューに蓄積し、ワーカーが後から処理できるサービスです。

たとえば注文システムで「注文作成」「決済完了」「キャンセル」をSNSに流し、発送ワーカーには決済完了だけを渡したいとします。すべての通知をSQSに入れてワーカーで捨てることもできますが、不要なメッセージをキューまで運ぶ必要はありません。

そこで使うのが、SNSのサブスクリプション(配信先の登録)に設定する FilterPolicy(フィルターポリシー) です。

この記事では2026年10月23日時点のAWS公式ドキュメントをもとに、CloudFormationによる構成、Node.jsからの送信、本文フィルタとの使い分け、見落としがちな権限や設定反映の注意点を整理します。

1. フィルターを置く場所

注文アプリからSNS TopicにイベントをPublish(送信)し、SNSは各SubscriptionのFilterPolicyを評価します。

注文アプリ
   |
   | SNS Publish
   v
OrdersTopic
   |
   +--> 決済完了だけ --> PaidOrdersQueue --> 発送ワーカー
   |
   +--> (別の購読先)キャンセルだけ --> CancellationQueue

重要なのは、FilterPolicyはTopic自体ではなく、Subscriptionごとに設定することです。同じTopicから異なる条件で複数のSQSキューへ配信できます。FilterPolicyを設定しないSubscriptionには、原則としてすべてのメッセージが配信対象になります。

2. MessageAttributesとMessageBodyの違い

フィルタリングする対象は FilterPolicyScope で選びます。

設定値 評価する対象 向いている設計
MessageAttributes(初期値) SNS Publish時のMessageAttributes イベント種別や送信元など、ルーティングに必要なメタ情報を独立管理する
MessageBody Messageに入れたJSONオブジェクト 既存のイベント本文の項目で振り分けたい

例として、次のFilterPolicyを考えます。

{
  "eventType": ["order.paid"],
  "source": ["checkout"]
}

この場合は両方のキーが一致する必要があります(キー間はAND)。同じキーの配列に複数値を指定した場合は、いずれかに一致すればよい条件(値の間はOR)として使えます。

MessageAttributesを使うなら、JSON本文に同じ値を書くだけでは一致しません。送信時に MessageAttributesとして指定する必要があります。

逆に MessageBody を指定した場合は、Messageの本文をJSONオブジェクトとして解釈して評価します。JSON文字列以外の本文や想定外の構造を送ると、条件に一致しません。

3. CloudFormationでSNS、SQS、FilterPolicyをまとめて定義する

CloudFormationはAWSのリソースをYAMLなどの設定ファイルで作成・更新する仕組みです。次の例は同一AWSアカウント・同一リージョンの標準SNS TopicとSQSキューを想定しています。

AWSTemplateFormatVersion: "2010-09-09"

Resources:
  OrdersTopic:
    Type: AWS::SNS::Topic

  PaidOrdersQueue:
    Type: AWS::SQS::Queue

  PaidOrdersQueuePolicy:
    Type: AWS::SQS::QueuePolicy
    Properties:
      Queues:
        - !Ref PaidOrdersQueue
      PolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Sid: AllowOnlyOrdersTopic
            Effect: Allow
            Principal:
              Service: sns.amazonaws.com
            Action: sqs:SendMessage
            Resource: !GetAtt PaidOrdersQueue.Arn
            Condition:
              ArnEquals:
                aws:SourceArn: !Ref OrdersTopic

  PaidOrdersSubscription:
    Type: AWS::SNS::Subscription
    DependsOn: PaidOrdersQueuePolicy
    Properties:
      TopicArn: !Ref OrdersTopic
      Protocol: sqs
      Endpoint: !GetAtt PaidOrdersQueue.Arn
      RawMessageDelivery: true
      FilterPolicyScope: MessageAttributes
      FilterPolicy:
        eventType:
          - order.paid
        source:
          - checkout

Outputs:
  TopicArn:
    Value: !Ref OrdersTopic
  QueueUrl:
    Value: !Ref PaidOrdersQueue

見るべき点は3つです。

① SQSのQueuePolicyが必要。 SNSからSQSへ届けるには、SQS側で sns.amazonaws.com に sqs:SendMessage を許可します。例では aws:SourceArn を特定のTopicのARNへ絞っています。これは「別のトピックを使って自分のキューへ送らせる」権限の混同を防ぐために重要です。

② FilterPolicyScopeは明示する。 初期値はMessageAttributesですが、設定ファイルに書けばチーム内で意図が共有できます。

③ RawMessageDeliveryはフィルタリングとは別設定。 trueならSQSに届く本文はSNSの通知ラッパーを含まない元メッセージになります。falseの場合、一般にSNSの通知情報を含むJSONがSQS本文に入ります。受信側のパーサーが変わるため、既存システムで切り替える際には注意してください。

上記の例は構成を理解するための最小形です。実運用ではSQSの暗号化、メッセージ保持期間、DLQ、監視、アプリケーション側IAM権限も検討します。

4. Node.js(AWS SDK for JavaScript v3)から送信する

次のコマンドはSDKのSNSクライアントをインストールします。

npm install @aws-sdk/client-sns

npm install は依存パッケージを追加するコマンドです。Node.jsプロジェクトにインストールしたうえで、次を publish.mjs として保存します。

import { SNSClient, PublishCommand } from "@aws-sdk/client-sns";

const topicArn = process.env.TOPIC_ARN;
if (!topicArn) {
  throw new Error("TOPIC_ARN が未設定です");
}

const client = new SNSClient({});

async function publishOrder(eventType) {
  const event = {
    eventType,
    orderId: "order-demo-001",
    occurredAt: new Date().toISOString(),
  };

  const result = await client.send(new PublishCommand({
    TopicArn: topicArn,
    Message: JSON.stringify(event),
    MessageAttributes: {
      eventType: {
        DataType: "String",
        StringValue: eventType,
      },
      source: {
        DataType: "String",
        StringValue: "checkout",
      },
    },
  }));

  console.log(eventType, result.MessageId);
}

await publishOrder("order.created");
await publishOrder("order.paid");

AWS SDKは通常、実行環境のIAMロールや開発環境の認証設定から認証情報とリージョンを取得します。コード内にアクセスキーを埋め込まないでください。送信元のIAM権限には対象Topicへの sns:Publish が必要です。

この例では、フィルタ設定が有効でキューへの配信権限が正しければ、PaidOrdersQueueへ配信対象となるのは order.paid のほうだけです。Publishが成功したという応答は、すべての購読先で処理が完了した証明ではありません。

実環境では注文IDを毎回同じデモ値にせず、業務イベントごとに一意なIDを持たせてください。

5. 本文の項目で絞り込みたい場合

発行元でMessageAttributesを付けられない場合や、既存イベントのJSON形式を使いたい場合は、Subscriptionを次のように変更できます。

FilterPolicyScope: MessageBody
FilterPolicy:
  eventType:
    - order.paid
  customer:
    plan:
      - premium

例えば Message に以下のJSONオブジェクトを文字列として送れば、eventTypeとcustomer.planの両方が一致するかで評価できます。

{
  "eventType": "order.paid",
  "orderId": "order-demo-001",
  "customer": {
    "plan": "premium"
  }
}

MessageBodyフィルタは入れ子のJSONに対応します。一方、MessageAttributesフィルタに同じ入れ子構造をそのまま持ち込むことはできません。属性によるルーティングはキーをフラットに保つ設計が分かりやすいです。

どちらを選んでも、フィルタは「受信先を振り分ける仕組み」であり、認可(アクセスを許可するかの判断)の代替ではありません。機密情報やユーザーの権限判断をフィルタ設定だけに依存させないでください。

6. 実務でハマりやすい4点

① 設定変更がすぐ効くとは限らない

AWS公式ドキュメントでは、SubscriptionのFilterPolicyの追加・変更が完全に反映されるまで最大15分かかる場合があると説明されています。

切り替え直後に古い条件で届いた/届かなかったとしても、即座にプログラムの不具合とは断定しないこと。移行時は古い形式も読める期間を設け、公開側と受信側に相関ID付きログを残すと追跡しやすくなります。

② 文字列の大文字・小文字と属性名がずれる

eventType と eventtype は別のキーです。また、order.paid と ORDER.PAID は、通常の文字列一致では同じ値とみなされません。スキーマを定数や型で共有し、テストで一致/不一致を確認してください。

③ RawMessageDeliveryと属性数に制約がある

AWS公式ドキュメントによると、SQS購読で RawMessageDelivery を有効にした場合、送信するメッセージ属性は最大10個です。属性が多い設計では、配信方式と制約を確認する必要があります。必要なルーティング情報だけ属性に出し、詳細データは本文に置くほうが保守しやすくなります。

④ フィルタで受け取らなかったメッセージは「処理失敗」とは違う

条件に一致しないイベントがそのキューへ届かないのは正常動作です。一方、条件に一致したのにキューへ配信できない場合は、SQSのポリシー、SNSのサブスクリプション状態、KMS権限などを確認します。コンシューマーの処理失敗に備えるSQSのDLQと、SNSから配信できなかった場合に備えるSNSサブスクリプションのRedrivePolicyは、役割が異なります。

また、通常のSNS/SQSでの非同期処理では重複配信を想定し、注文IDとイベント種別等を用いた冪等処理(同じイベントを複数回処理しても結果が崩れない作り)を検討します。

7. 本番に入れる前の確認

以下を、CloudFormation適用後の結合試験で確認するのが実務的です。

テスト 期待結果
order.created / source=checkout PaidOrdersQueueには配信されない
order.paid / source=checkout PaidOrdersQueueに配信される
order.paid / source=other PaidOrdersQueueには配信されない
source属性なし PaidOrdersQueueには配信されない
フィルタ設定変更の直後 反映遅延を許容して観測し、即時切替を前提にしない
同一イベントを再受信 ワーカーが安全に重複を扱える

ここに示したコードとテンプレートは公式API仕様に基づく構成例であり、この原稿作成時点では実際のAWSアカウントへのデプロイ/配信テストは実施していません。環境ごとのIAM、暗号化、クロスアカウント設定は個別に検証してください。

まとめ

SNSのFilterPolicyを使えば、必要なイベントを必要な購読先へ届ける構成を、アプリケーション側の複雑な振り分け処理なしで表現できます。

実装で特に大切なのは、フィルタ対象(属性か本文)を揃えること、SQS側の送信許可を正しく絞ること、設定の反映遅延を前提にすることです。フィルタを設定したら終わりではなく、受信処理の冪等性や障害時の追跡まで含めて設計しましょう。

参考資料(2026年10月23日確認)

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?