Task: Decompress payloads in jetstream_admin peek_message/peek_last_message
Table of Contents
This page documents a task in the Fix jetstream_admin missing gzip decompression story. It captures the goal, current status, acceptance, and any notes or results.
Goal
Since PR #1706, js_publish() transparently gzips any payload at or
above 4096 bytes (the compression task's threshold). The admin
"peek" path (jetstream_admin::peek_message=/=peek_last_message)
never grew the matching decompression step extract_message() got in
the same PR – it reads natsMsg data directly and doesn't even
extract headers, so it has no way to know a payload is compressed.
Result: an admin inspecting a queued message that happened to cross
the threshold (e.g. ores.qt's QueueDetailDialog) now sees raw
gzip bytes instead of the JSON/content they expect, with no
indication decompression was skipped.
Fix: extract headers in peek_message=/=peek_last_message the same
way extract_message() does, then run the payload through
decompress_if_flagged() before returning stream_message.
Status
| Field | Value |
|---|---|
| State | DONE |
| Parent story | Fix jetstream_admin missing gzip decompression |
| Now | Nothing. |
| Waiting on | Nothing. |
| Next | Nothing. |
| Last touched | 2026-07-27 |
Acceptance
[X]peek_message()andpeek_last_message()extract headers and calldecompress_if_flagged()before returningstream_message, same asextract_message().[X]A JetStream message above the 4096-byte compression threshold decompresses correctly through the peek path (verified by construction: both peek methods now route through the same header-extraction +decompress_if_flagged()logic asextract_message(), anddecompress_if_flagged()itself is unit-tested indomain_compression_tests.cpp).
Plan
Added a fill_data_and_headers() helper in
jetstream_admin.cpp's anonymous namespace, mirroring
client.cpp's extract_message(): extracts the natsMsg's headers
into stream_message::headers (already present on the domain type,
just unpopulated by the peek path), then runs the data through
decompress_if_flagged(). Both peek_message() and
peek_last_message() call the shared helper instead of duplicating
the raw-data-copy they had before, so they now decompress exactly
like every other inbound message path.
jetstream_admin has no existing unit tests (it wraps a live
JetStream context and is exercised via the QA Validation Runner
instead, per the * Test Scenarios table below) so no new unit test
was added; local build (ores.nats.lib) and the existing
ores.nats.tests suite both pass.
Notes
Test Scenarios
Manual QA scenarios (scaffolded via compass add test_scenario, run
through the QA Validation Runner panel) that verify this task. Link
new ones here as they're created; the scenario doc itself links back
via its "Verifies task" field.
| Scenario | State | Notes |
|---|---|---|
PRs
| PR | Title |
|---|---|
| #1736 | [ores.nats] Decompress jetstream_admin peek payloads |
Review
| # | Comment summary | File | Decision | Notes |
|---|---|---|---|---|
| 1 | natsMsg leak if decompress_if_flagged() throws on malformed gzip (raised independently by 2 reviewers) | jetstream_admin.cpp | Fixed | Added nats_msg_guard RAII (unique_ptr with natsMsg_Destroy deleter) in both peek functions, mirroring client.cpp's on_msg() exception-safety handling. |
| 2 | free(keys) used without an explicit #include <cstdlib> | jetstream_admin.cpp | Fixed | Added the include, matching client.cpp's convention. |
Result
jetstream_admin::peek_message() and peek_last_message() now
extract headers and call decompress_if_flagged() before returning
stream_message, via a shared helper mirroring client.cpp's
extract_message(). Admin tooling (ores.qt's QueueDetailDialog)
will see decompressed content for any JetStream message above the
4096-byte compression threshold, instead of raw gzip bytes.
Review round 1 caught a genuine resource-safety gap: since
decompress_if_flagged() can throw on a gzip-flagged-but-malformed
payload, calling it before natsMsg_Destroy(msg) would leak msg on
that path. Fixed with a nats_msg_guard RAII wrapper
(std::unique_ptr with natsMsg_Destroy as deleter), mirroring the
same exception-safety handling client.cpp's on_msg() already has
for this exact hazard. Also added the missing #include <cstdlib>
for free(), matching client.cpp's convention.
Local build (ores.nats.lib) and ores.nats.tests both pass; no new
unit test added since jetstream_admin has no existing unit-test
coverage (live-JetStream-only, exercised via manual QA).