How do you make one Amazon SNS subscription receive only a subset of a topic's messages, and what is the difference between filtering on message attributes and on the message body?
answer
- the filter lives on the subscription
- keys AND-ed, values OR-ed
- missing key means no match
- attributes by default, body by opt-in
- filtered out means never delivered
basics
~20 sAttach a filter policy to the subscription. It is a JSON document of accepted values that SNS evaluates before delivery, either against the publisher's message attributes (the default) or against the JSON message body when the subscription's filter policy scope is set to the body.
solid answer
~50 sFiltering in SNS lives on the subscription, not the publisher. You set a `FilterPolicy` — a JSON object mapping keys to arrays of accepted values — and SNS delivers only messages that match, so a subscriber never pays to receive and discard events it does not want. By default the policy is evaluated against the message attributes the publisher set on `Publish`; setting `FilterPolicyScope` to `MessageBody` evaluates the same style of policy against the JSON body instead, which is useful when you do not control the publisher enough to add attributes. Semantics matter: every key named in the policy must match, values within a key are OR-ed, and a message that lacks a named key does not match at all unless you use the `exists` operator. Beyond exact strings you get `prefix`, `anything-but`, numeric ranges and `exists`.
code
json · 6 lines{
"eventType": ["OrderPlaced", "OrderCancelled"],
"region": [{"prefix": "eu-"}],
"totalCents": [{"numeric": [">=", 10000]}],
"testRun": [{"exists": false}]
}go deeper
Know that a subscription can carry a filter policy so a subscriber gets only some of a topic's messages, and that it is JSON listing accepted values per key. Being able to read a simple policy aloud is enough here.
Explain the evaluation rules precisely — keys AND-ed, values OR-ed, a missing key failing to match — and the difference between attribute scope and body scope. Expect to be asked which operators exist beyond exact match.
Demonstrate that you treat filtering as a cost and blast-radius lever and that you can debug its silent failure mode: correlate the filtered-out metric with publishes, and catch type or case mismatches introduced by a publisher change.
Own the contract question: whether routing metadata is standardised as attributes across teams, who may change it, how many topics versus how many filtered subscriptions your platform should carry, and when filtering has quietly become a routing layer that needs a different primitive.
## Where filtering happens In Amazon SNS every subscription receives every message published to the topic — unless that subscription carries a **filter policy**. The policy is a subscription attribute (`FilterPolicy`), so the publisher is unaware of it: producers keep publishing one event type to one topic, and each consumer declares what it cares about. That is the property that makes topic-per-domain designs survive; without it, teams create a topic per event type and the topic count explodes. ## The shape of a policy A filter policy is a JSON object whose keys are attribute (or body) fields and whose values are arrays of accepted values: ```json { "eventType": ["OrderPlaced", "OrderCancelled"], "region": [{"prefix": "eu-"}], "totalCents": [{"numeric": [">=", 10000]}] } ``` The evaluation rules are the part interviewers probe: - **Keys are AND-ed.** Every key present in the policy must match for delivery to happen. - **Values inside a key are OR-ed.** `"eventType": ["A", "B"]` means A or B. - **A missing key is not a match.** If the message carries no `region`, the policy above rejects it. To express "only when absent", use `{"exists": false}`; for "present, any value", `{"exists": true}`. - **Types are strict.** A numeric operator against a value the publisher sent as a string will not match, which is the single most common cause of "my filter silently drops everything". Besides exact string matching, the operators you should be able to name are `prefix`, `suffix`, `anything-but`, `numeric` (with `<`, `<=`, `=`, `>=`, `>` and range form), and `exists`. ## Attribute scope versus body scope By default `FilterPolicyScope` is `MessageAttributes`: the policy is matched against the `MessageAttributes` map the publisher supplied on `Publish`. This keeps the routing metadata explicitly separated from the payload, which is the cleaner contract — a consumer's filter never depends on the internal shape of someone else's JSON. Setting `FilterPolicyScope` to `MessageBody` matches the policy against the message body parsed as JSON, using nested objects in the policy to reach nested fields. This is the escape hatch for events you do not control: a third-party publisher, a legacy service, or a payload that already carries the discriminator. The price is coupling — the subscription now breaks when the publisher renames a field — and the body must be valid JSON. A subscription has one scope at a time; you cannot mix a body condition and an attribute condition in one policy. ## Why it matters beyond tidiness Filtering is a **cost and blast-radius** control, not just convenience. Messages filtered out are never delivered, so a Lambda subscriber is not invoked, an SQS queue does not accumulate work its consumer will throw away, and an HTTPS endpoint is not woken up. On high-volume topics this is the difference between one and ten million downstream invocations. SNS exposes `NumberOfNotificationsFilteredOut` in CloudWatch so you can see how much a policy is actually removing — and, when it reads suspiciously like the publish count, that your policy is wrong. ## Practical traps 1. **Publisher forgets the attribute.** A refactor drops `MessageAttributes` from one code path; that path's events quietly stop reaching every filtered subscriber. Nothing errors — the messages are simply filtered out. 2. **String versus number.** `"429"` published as a string will never match a numeric policy. 3. **Case sensitivity.** Matching is case sensitive on both keys and values. 4. **Filtering is not transformation.** SNS decides deliver-or-not; it does not reshape the payload for a subscriber. 5. **Raw message delivery interacts, not conflicts.** With raw delivery to SQS, message attributes are still carried across as SQS message attributes, so attribute-scope filtering and raw delivery coexist happily. 6. **Debugging.** Test a policy against a sample message before shipping — the failure mode of a bad filter policy is silence, not an error, and silence is the hardest failure to notice in a pub/sub system. ## When the filter is doing routing's job If a topic accumulates a dozen subscriptions each with an elaborate content-matching policy, and events arrive from AWS services or SaaS partners rather than your own publishers, you are approximating a content router with a fanout primitive. That is the point where the service choice itself deserves a second look.
- A subscriber suddenly receives nothing, but publishes continue and no errors appear anywhere. How do you confirm a filter policy is the cause?Check the topic's `NumberOfNotificationsFilteredOut` metric against `NumberOfMessagesPublished` — if they track each other, the policy is rejecting everything. Then read the subscription's `FilterPolicy` and a sample published message side by side, looking for a renamed attribute, a value sent as a string where the policy expects a number, or a case mismatch.
- Why prefer message-attribute filtering over body filtering when you control both sides?Attributes are an explicit routing contract kept separate from the payload, so consumers do not couple to the internal JSON shape and publishers can evolve the body freely. Body scope is the escape hatch for events you do not control; use it knowingly, because a field rename in someone else's payload silently breaks your subscription.
- Does a filter policy reduce your bill, or just your consumer's work?Both. Filtered-out messages are not delivered, so you avoid the downstream cost entirely — no Lambda invocation, no SQS request, no HTTPS call and no processing time. On a high-volume topic the saved delivery and downstream compute usually dwarfs any saving on the publish side.
saying these in an interview costs you the question
- The publisher decides which subscribers receive a message
- A message missing the filtered attribute is delivered anyway
- Filter policies can transform or reshape the payload
- Numeric comparison works on values published as strings
- You can combine body and attribute conditions in one policy