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() and peek_last_message() extract headers and call decompress_if_flagged() before returning stream_message, same as extract_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 as extract_message(), and decompress_if_flagged() itself is unit-tested in domain_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).

Emacs 29.3 (Org mode 9.6.15)