Repository navigation
Publish SNS headers the way SQS subscribers read them - #108
Open
florianPat wants to merge 1 commit into
Open
florianPat wants to merge 1 commit into
florianPat wants to merge 1 commit into
Conversation
A topic published to by SnsTransport cannot be consumed from a subscribed SQS queue. The transport puts every header into a single "Headers" message attribute, which is the convention SnsConsumer reads. SqsConsumer and Symfony's own Amazon SQS transport read a different one: an "X-Symfony-Messenger" attribute holding the headers that cannot be sent individually, plus one attribute per remaining header. Because "Headers" is not "X-Symfony-Messenger", it falls through to the second step and arrives as a single header literally named "Headers" whose value is the JSON of the real ones. The envelope therefore has no "type" header and decoding fails. SNS to SQS fan-out is a common enough setup that the two consumers this package ships should agree, so send() now writes both conventions. The "Headers" attribute is untouched, so SnsConsumer and messages already in flight are unaffected. Which headers can travel as their own attribute is decided by the rules AWS documents for attribute names, mirroring Connection::send() in symfony/amazon-sqs-messenger, which this package already depends on. Symfony's serializer emits stamp headers such as "X-Message-Stamp-Symfony\Component\...", and a backslash is not valid in an attribute name, so those go into the aggregate instead. So does a header with an empty value, which SNS rejects outright. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Wbzh5AJ1EjPE62vYwUsPUZ
florianPat
force-pushed
the
fix/sns-to-sqs-header-round-trip
branch
from
August 30, 2026 08:52
586dfbf to
7f584b2
Compare
Member
|
I'll leave this one for review by other users of this package. Let's give it a week or two. If no-one has voiced an opinion here, I'll merge it (feel free to ping me then because I will likely have forgotten 😅) |
Member
|
(oh btw is there an issue this would fix?) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The problem
A message published by
SnsTransportcannot be consumed from an SQS queue subscribed to that topic — neither with this package's ownSqsConsumernor with Symfony's Amazon SQS transport.SnsTransport::send()puts the whole header set into one message attribute namedHeaders:That is the convention
SnsConsumerreads.SqsConsumerreads a different one, the onesymfony/amazon-sqs-messengerwrites: anX-Symfony-Messengerattribute holding the headers that cannot be sent individually, and then one attribute per remaining header.Headersis notX-Symfony-Messenger, so it falls through to the second loop and arrives as a single header literally namedHeaders, whose value is the JSON of the real ones. The envelope ends up without atypeheader andSerializer::decode()fails.SNS to SQS fan-out is a normal way to use SNS, and both consumers ship in this package, so the mismatch is worth closing here rather than in every application.
The change
send()now writes both conventions. TheHeadersattribute is published exactly as before, soSnsConsumerand any message already in flight are unaffected; alongside it the headers are published the way an SQS subscriber expects.Which headers can travel as their own attribute is decided by the rules AWS documents for attribute names. That check mirrors
Connection::send()insymfony/amazon-sqs-messenger, which this package already requires, so the two stay comparable:X-Message-Stamp-Symfony\Component\Messenger\Stamp\DelayStamp, and a backslash is not valid in an attribute name.HeadersandX-Symfony-Messengerthemselves would collide with the two aggregates.Anything in those categories goes into the
X-Symfony-Messengeraggregate instead, so no header is lost.Tests
Four functional tests in
SnsTransportTest, including one that reconstructs the headers the waySqsConsumerdoes and asserts they match what was published.vendor/bin/phpunitis green (45 tests) andvendor/bin/phpstanreports no errors.Trade-off worth naming
The headers are now on the wire twice: once in
Headers, once as attributes or inX-Symfony-Messenger. That costs a few hundred bytes of the 256 KB attribute budget and two to three of the ten available attributes.The alternative would be to teach
SnsConsumertheX-Symfony-Messengerconvention and dropHeaders, which avoids the duplication but breaks every message published by an older version of this package while a deployment is in flight. I went for the compatible option, but I am happy to switch if you would rather make it a clean cut, or put the new behaviour behind a transport option.