Skip to content

feat(exporterhelper): add queue payload codec hook (SAW-6556 backport)#5

Merged
amir-jakoby merged 3 commits into
mainfrom
saw-6556-backport-0.140
Mar 3, 2026
Merged

feat(exporterhelper): add queue payload codec hook (SAW-6556 backport)#5
amir-jakoby merged 3 commits into
mainfrom
saw-6556-backport-0.140

Conversation

@amir-jakoby

@amir-jakoby amir-jakoby commented Mar 2, 2026

Copy link
Copy Markdown

Summary

  • backport queue payload codec hook into exporterhelper on current Sawmills baseline
  • add QueueBatchPayloadCodec interface and WithQueueBatchPayloadCodec option
  • include tests for marshal/unmarshal wrapping behavior

Validation

  • cd exporter/exporterhelper && go test ./...

Context

Backport branch for SAW-6556 to avoid full collector/contrib uplift in this ticket.


Summary by cubic

Adds a queue payload codec hook in exporterhelper to enable optional payload compression for sending queues. Supports SAW-6556 (loadbalancingexporter-only) without changing default behavior.

  • New Features

    • Added QueueBatchPayloadCodec and WithQueueBatchPayloadCodec to wrap Marshal/Unmarshal (Encode on enqueue, Decode on dequeue).
    • Wired codec into BaseExporter when provided; applies only if queue Encoding is set, avoids double-wrapping, and is order-independent.
    • Included tests for roundtrip behavior and invalid payload handling.
  • Refactors

    • Updated changelog entry to use a valid component key.

Written for commit 1db2895. Summary will update on new commits.

Summary by CodeRabbit

Release Notes

  • New Features

    • Queue batch processing now supports custom payload codec configuration through the WithQueueBatchPayloadCodec option
    • Introduced QueueBatchPayloadCodec interface enabling custom encoding/decoding transformations on serialized queue payloads
  • Tests

    • Added test coverage validating custom payload codec encode/decode behavior

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@coderabbitai

coderabbitai Bot commented Mar 2, 2026

Copy link
Copy Markdown

Walkthrough

The changes introduce a new queue payload codec mechanism for batch exporters. A public QueueBatchPayloadCodec interface enables custom encoding/decoding of queue payloads, integrated through a WithQueueBatchPayloadCodec option. The internal implementation wraps the existing queue encoding with a payloadCodecEncoding adapter that applies codec transformations during enqueue and dequeue operations, with comprehensive test coverage validating the encoding/decoding behavior.

Changes

Cohort / File(s) Summary
Public API
exporter/exporterhelper/queue_batch.go
Added public QueueBatchPayloadCodec interface with Encode and Decode methods, and WithQueueBatchPayloadCodec option function to configure custom payload transformation.
Internal Implementation
exporter/exporterhelper/internal/base_exporter.go
Added internal queuePayloadCodec interface, payloadCodecEncoding wrapper type that composes queue.Encoding[request.Request], and codec integration in WithQueueBatch to apply codec transformations around marshaling/unmarshaling.
Test Coverage
exporter/exporterhelper/internal/base_exporter_test.go
Added TestWithQueueBatchPayloadCodec test function with prefixCodec helper type to validate payload encoding and decoding behavior.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 Hoppy hops through batches with delight,
Codecs wrap the payloads, tight and tight!
Encode on the way in, decode way out,
Queue transformations—that's what it's about! 🎉

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: adding a queue payload codec hook to exporterhelper, with a backport reference (SAW-6556).
Description check ✅ Passed The pull request description provides clear context including a summary of changes, validation steps, and links to related work.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch saw-6556-backport-0.140

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cubic analysis

1 issue found across 3 files

Confidence score: 3/5

  • Order-dependent behavior in exporter/exporterhelper/internal/base_exporter.go means WithQueueBatchPayloadCodec can be silently ignored if passed after WithQueue/WithQueueBatch, which could change runtime behavior unexpectedly.
  • This is a concrete user-facing configuration pitfall (severity 6/10), so there’s some merge risk despite being localized.
  • Pay close attention to exporter/exporterhelper/internal/base_exporter.go - option ordering can cause the payload codec to be skipped.
Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="exporter/exporterhelper/internal/base_exporter.go">

<violation number="1" location="exporter/exporterhelper/internal/base_exporter.go:258">
P2: `WithQueueBatchPayloadCodec` is order-dependent and can be silently ignored when provided after `WithQueue`/`WithQueueBatch`.</violation>
</file>

Linked issue analysis

Linked issue: SAW-6556: Compressed Sending Queue + file_storage for Collector exporters

Status Acceptance criteria Notes
Add optional queue payload compression for loadbalancingexporter only. No LB exporter integration present in diffs
⚠️ Validate compatibility with persistent sending queue (sending_queue.storage: file_storage/...) for burst absorption with bounded RAM. Hook wraps encoding for persistent queue but no validation tests
Provide benchmark evidence of tradeoffs for this exporter path. No benchmarks or results included
Implement optional payload compression path in loadbalancingexporter queue handling: sending_queue.payload_compression: none|snappy|zstd (default none). No config or LB exporter code added
⚠️ Ensure compatibility with existing queue semantics: ordering, retry/backoff, shutdown/drain, restart/recovery for persistent queue. Codec is integrated into encoding but semantics tests are missing
Keep existing sending_queue.storage semantics (file_storage/), do not introduce memory|file_storage enum. No storage enum introduced; persistent-encoding check retained
Add telemetry for: enqueue/dequeue bytes (raw vs stored), compression ratio, compression/decompression CPU time. No telemetry additions in diffs
Feature off by default; no behavior change when disabled. Codec applied only when provided; nil leaves behavior unchanged
Applies only to loadbalancingexporter. Hook added generically to exporterhelper, not LB-only
⚠️ Correctness tests pass for: enqueue/dequeue integrity. Unit test for Marshal/Unmarshal exists but not full enqueue/dequeue tests
Correctness tests pass for: retry behavior with compressed entries. No retry behavior tests for compressed entries
Correctness tests pass for: persistent queue restart/recovery. No restart/recovery tests present
Benchmarks at minimum: 14k EPS (stable). No benchmark artifacts or results
Benchmarks: 16k EPS (stress). No benchmark artifacts or results
Benchmark variant: memory queue + none. No benchmark variants provided
Benchmark variant: memory queue + snappy (and/or zstd). No benchmark variants provided
Benchmark variant: file_storage queue + none. No benchmark variants provided
Benchmark variant: file_storage queue + compression. No benchmark variants provided
Report: achieved EPS, p95/p99, error/fallback rates. No report or metrics included
Report: collector CPU and queue fill slope. No report or metrics included
Report: RSS and disk usage. No report or metrics included
Report: codec break-even guidance. No analysis or guidance provided
Implementation in LB exporter path. No modifications to loadbalancingexporter found
Tests + benchmark artifacts. Only unit tests for encoding exist; no benchmarks
Tuning/failure-mode documentation for operators. No documentation added in diffs

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Comment thread exporter/exporterhelper/internal/base_exporter.go

@sawmills-architect-review sawmills-architect-review Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[ARCH-REVIEW] REQUEST_CHANGES — 1 concern, 1 nit.

Clean backport conceptually: the payloadCodecEncoding wrapper is well-structured, the interface is minimal and correct, and the roundtrip test covers the happy path + invalid prefix case. One silent footgun in the option wiring order needs to be fixed before merge.

Comment thread exporter/exporterhelper/internal/base_exporter.go Outdated
Comment thread exporter/exporterhelper/queue_batch.go

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
exporter/exporterhelper/internal/base_exporter.go (1)

214-233: ⚠️ Potential issue | 🟠 Major

Codec hook is order-dependent and can be silently skipped.

WithQueueBatchPayloadCodec only sets a field, while wrapping happens inside WithQueueBatch. If callers pass WithQueueBatch(...) before WithQueueBatchPayloadCodec(...), the codec is never applied.

💡 Proposed fix (apply wrapping after all options are parsed)
 func NewBaseExporter(set exporter.Settings, signal pipeline.Signal, pusher sender.SendFunc[request.Request], options ...Option) (*BaseExporter, error) {
 	be := &BaseExporter{
 		Set:        set,
 		timeoutCfg: NewDefaultTimeoutConfig(),
 	}

 	for _, op := range options {
 		if err := op(be); err != nil {
 			return nil, err
 		}
 	}
+
+	if be.queuePayloadCodec != nil && be.queueBatchSettings.Encoding != nil {
+		be.queueBatchSettings.Encoding = payloadCodecEncoding{
+			encoding: be.queueBatchSettings.Encoding,
+			codec:    be.queuePayloadCodec,
+		}
+	}

 	// Consumer Sender is always initialized.
 	be.firstSender = sender.NewSender(pusher)
@@
 func WithQueueBatch(cfg queuebatch.Config, set queuebatch.Settings[request.Request]) Option {
 	return func(o *BaseExporter) error {
 		if !cfg.Enabled {
 			o.ExportFailureMessage += " Try enabling sending_queue to survive temporary failures."
 			return nil
 		}
-		if o.queuePayloadCodec != nil && set.Encoding != nil {
-			set.Encoding = payloadCodecEncoding{
-				encoding: set.Encoding,
-				codec:    o.queuePayloadCodec,
-			}
-		}
 		if cfg.StorageID != nil && set.Encoding == nil {
 			return errors.New("`Settings.Encoding` must not be nil when persistent queue is enabled")
 		}
 		o.queueBatchSettings = set
 		o.queueCfg = cfg
 		return nil
 	}
 }

Also applies to: 256-261

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@exporter/exporterhelper/internal/base_exporter.go` around lines 214 - 233,
The issue is that wrapping the queue settings with the payload codec is
order-dependent: WithQueueBatch currently wraps only when the codec was set
first, so callers that apply WithQueueBatch before WithQueueBatchPayloadCodec
silently skip the codec; fix by making the codec application idempotent in both
places: keep WithQueueBatch assigning o.queueBatchSettings and o.queueCfg (but
do not expect codec order), and update WithQueueBatchPayloadCodec to, after
setting o.queuePayloadCodec, check if o.queueBatchSettings.Encoding != nil and
then wrap o.queueBatchSettings.Encoding with payloadCodecEncoding (using the
same wrapper logic currently in WithQueueBatch), so whichever option runs second
will apply the codec to the already-stored settings; reference functions/fields:
WithQueueBatch, WithQueueBatchPayloadCodec, BaseExporter.queuePayloadCodec,
BaseExporter.queueBatchSettings, payloadCodecEncoding.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@exporter/exporterhelper/internal/base_exporter.go`:
- Around line 214-233: The issue is that wrapping the queue settings with the
payload codec is order-dependent: WithQueueBatch currently wraps only when the
codec was set first, so callers that apply WithQueueBatch before
WithQueueBatchPayloadCodec silently skip the codec; fix by making the codec
application idempotent in both places: keep WithQueueBatch assigning
o.queueBatchSettings and o.queueCfg (but do not expect codec order), and update
WithQueueBatchPayloadCodec to, after setting o.queuePayloadCodec, check if
o.queueBatchSettings.Encoding != nil and then wrap o.queueBatchSettings.Encoding
with payloadCodecEncoding (using the same wrapper logic currently in
WithQueueBatch), so whichever option runs second will apply the codec to the
already-stored settings; reference functions/fields: WithQueueBatch,
WithQueueBatchPayloadCodec, BaseExporter.queuePayloadCodec,
BaseExporter.queueBatchSettings, payloadCodecEncoding.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between cc6d049 and 8ffd61d.

📒 Files selected for processing (3)
  • exporter/exporterhelper/internal/base_exporter.go
  • exporter/exporterhelper/internal/base_exporter_test.go
  • exporter/exporterhelper/queue_batch.go

@sawmills-architect-review sawmills-architect-review Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[ARCH-REVIEW] Re-review: No blockers. Prior concern resolved, one nit.

The option-ordering footgun (🟡 from prior review) is fully addressed — applyQueuePayloadCodec() now runs in NewBaseExporter after all options are applied, and the double-wrap guard is clean. Both ordering variants are tested. All threads resolved. One 🟢 nit on duplicate interface definitions flagged inline.

Comment thread exporter/exporterhelper/internal/base_exporter.go
@amir-jakoby

Copy link
Copy Markdown
Author

Addressed latest thread:

  • kept internal interface separate from public because cannot import (would introduce package cycle).
  • contract remains identical and covered by tests; leaving this as-is for minimal backport churn.

@amir-jakoby

Copy link
Copy Markdown
Author

Addressed latest thread:

  • kept the internal queue payload codec interface separate from the public one because exporterhelper/internal cannot import exporterhelper without a package cycle.
  • contract remains identical and covered by tests; leaving this as-is for minimal backport churn.

@sawmills-architect-review sawmills-architect-review Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[ARCH-REVIEW] ✅ All review threads resolved and concerns addressed. Approved.

@amir-jakoby

Copy link
Copy Markdown
Author

CI sweep update:

  • review threads resolved and PR is approved.
  • changelog gate now passes.
  • remaining failing checks are not introduced by this diff:
    1. govulncheck (GO-2026-4394 in cmd/mdatagen on current branch baseline).
    2. contrib-tests-prepare (prepare-contrib scraping helper package mismatch in current fork baseline).
      I did not change unrelated modules in this PR to keep scope isolated.

@amir-jakoby amir-jakoby merged commit 0f359aa into main Mar 3, 2026
42 of 47 checks passed
@amir-jakoby amir-jakoby deleted the saw-6556-backport-0.140 branch March 3, 2026 00:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant