fix(messaging): Fault<T> payload shape, consume-side header fidelity, and TTL expiration#306
Merged
Merged
Conversation
…ormalize header types and TTL expiration across consume paths
Code reviewNo issues found. Checked for bugs and CLAUDE.md compliance. |
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.
Summary
Fixes three wire-fidelity defects in the messaging stack so the documented contracts hold against real broker traffic:
Fault<T>.Messagenow carries the original message payload. The RabbitMQ fault publisher used to embed the entire wire envelope, soJsonSerializer.Deserialize<Fault<TMessage>>against a real fault yielded default/missing fields; the only E2E assertion substring-matched an id, which passed because the envelope contained it. The worker now unwraps the envelope (reusing the already-parsed one from dispatch) and the docs' "original message body" promise is true. Bare-JSON (non-Vulthil) deliveries keep the whole-body behavior.JsonElements and bare-AMQP deliveries surfacedbyte[]for strings —AddHeader("tenant", "acme")was never astringagain on the consume side, and the shape differed by path. Now strings/booleans arrive asstring/booland numbers asint/long/double(narrowest fit) on the envelope path, the bare-AMQP path, and the outbox relay path alike; objects/arrays surface asJsonElement. Contract documented onIMessageContext.Headers.ExpirationTimeis anchored to the publish timestamp. The AMQP per-message TTL is relative to publish, but was mapped toUtcNow + ttlat consume time, systematically overstating expiry; it now usesSentTime + ttlwhen the delivery carries a timestamp, with the old behavior as fallback.longregardless of width and complex values as raw-JSON strings (re-published as escaped strings). It now aligns with the consume-side normalization and rematerializes complex values as detachedJsonElements, so a relayed message's header bytes and consumed types are identical to a directly-published one.Broker-path outbox rows already stamp
TraceParent/TraceStateand the relay already restores them; the outbox article now documents that preservation. No public API changes; new helpers are internal.Observable wire-format changes (release notes)
Fault<T>.Messageis the original message object, no longer theMessageEnvelopewrapper ({"messageId": …, "message": {…}}nesting is gone).IMessageContext.Headersvalues:string/bool/int/long/doubleprimitives (previouslyJsonElementon envelope deliveries,byte[]strings on bare-AMQP deliveries). Integral values narrower than before where they fit inInt32.IMessageContext.ExpirationTimeon TTL-bearing bare-AMQP deliveries: computed from the publish timestamp instead of the consume-time clock (earlier, more accurate instants).Verification
src/and re-running): fault-payload capture through the real worker/channel seam with fullFault<T>deserialization, header-fidelity matrix ({string, int, long, double, bool, Guid-as-sent, complex} × {envelope, bare AMQP, outbox relay, in-memory harness}), TTL-anchoring, and a relayed-equals-direct consumed-headers equivalence test.Fault<PoisonCommand>deserialization.Backport to v1.0: no — wire-content changes are minor-appropriate; ships in 1.1.0.