Skip to content

out_stackdriver: fix batch drop on invalid labels - #11539

Open
erain wants to merge 3 commits into
fluent:masterfrom
erain:fix/stackdriver-record-drop
Open

out_stackdriver: fix batch drop on invalid labels#11539
erain wants to merge 3 commits into
fluent:masterfrom
erain:fix/stackdriver-record-drop

Conversation

@erain

@erain erain commented Mar 11, 2026

Copy link
Copy Markdown
Contributor

Description

When a single record contains a logging.googleapis.com/labels field that is not a map, the out_stackdriver plugin currently drops the entire batch. This causes data loss for all other valid records in that batch.

Root Cause

In stackdriver_format(), the labels type check had two problems:

  1. It was checked after operation, sourceLocation, and httpRequest extraction, requiring complex cleanup on the error path (which was also missing destroy_http_request(), causing a memory leak).
  2. The same drop decision was duplicated across the prescan loop and main packing loop, creating fragile, divergent validation paths.

Fix

Extracted a shared should_skip_record() helper that validates both insertId and labels in one place:

static int should_skip_record(struct flb_stackdriver *ctx,
                              msgpack_object *obj,
                              int log_errors)

This helper is now used:

  • In the prescan loop (silent, log_errors=FLB_FALSE) to correctly compute array_size (entries count).
  • At the top of the main packing loop (log_errors=FLB_TRUE), before any field extraction, so invalid records are skipped cleanly with no cleanup needed.

Metrics

The number of formatted records is reported back to cb_stackdriver_flush() so processed, retry, and dropped-record metrics reflect the records actually sent:

  • Records skipped by should_skip_record() are added to the dropped-records counter once the flush completes without retry.
  • A batch where all records are invalid is treated as locally dropped (FLB_OK + dropped-records counter) instead of a retryable formatter failure, which previously caused infinite retries of an unformattable chunk.

Scope

This PR fixes only the labels not a map per-record drop case.

The following batch-level drop scenarios remain unchanged as they affect shared batch-level state by design:

  • flb_log_event_decoder_init() failure
  • flb_log_event_decoder_next() failure
  • k8s local_resource_id extraction/processing failures (k8s_container, k8s_node, k8s_pod)
  • process_local_resource_id() failures
  • Final msgpack-to-JSON serialization failure

Testing

New Tests (7 total)

Test Scenario
labels_not_a_map Single record with labels as string → no output
labels_not_a_map_custom_key Non-default labels_key with string value → no output
labels_not_a_map_with_extracted_fields Record with invalid labels + httpRequest, operation, sourceLocation, trace, spanId → exercises skip path with extracted fields present
batch_labels_not_a_map 3-record batch (valid/invalid/valid) → 2 entries, bad record dropped
batch_first_record_labels_not_a_map First record invalid, second valid → only valid record emitted
batch_all_records_labels_not_a_map All records have invalid labels → no output
batch_mixed_errors 4-record batch mixing invalid insertId + invalid labels + valid → only valid records emitted

Batch tests with content assertions (batch_labels_not_a_map, batch_first_record_labels_not_a_map, batch_mixed_errors) use tail/file-backed fixtures to guarantee single-batch semantics. The batch_all_records_labels_not_a_map test uses a fixture file as well but only asserts no output.

Regression Tests

Full flb-rt-out_stackdriver suite passes locally after rebasing onto current master (including the recent stackdriver memory-leak fixes, SDS append checks, and grouped-log counter parity changes).

Summary by CodeRabbit

  • Bug Fixes

    • Improved entry validation: records with invalid or missing insertId or non-map labels are skipped early; prescan suppresses duplicate logs. If all records are rejected a warning is emitted, dropped-record metrics are incremented, and the batch is not retried. Per-record metrics now track formatted, skipped, failed, and successful counts with safeguards.
  • Tests

    • Added runtime tests for single-record and batch scenarios covering invalid labels, mixed-error batches, first-record-drop cases, and custom-labels-key behavior.

@coderabbitai

coderabbitai Bot commented Mar 11, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds a centralized per-record validator should_skip_record() to the Stackdriver output plugin, uses it during prescan and formatting to drop invalid records (invalid insertId or non-map labels), propagates formatted/skipped counts into flush/metrics, and adds runtime tests and payloads for invalid-label scenarios.

Changes

Stackdriver plugin refactor

Layer / File(s) Summary
Validator and prescan
plugins/out_stackdriver/stackdriver.c
Add should_skip_record() for insertId/labels validation; initialize *formatted_records and use validator in prescan to decrement accepted entry count and early-return when zero accepted.
Per-record formatting and packing
plugins/out_stackdriver/stackdriver.c
Skip invalid records during entry packing via should_skip_record(..., FLB_TRUE); refactor insertId extraction, move labels extraction, and set final *formatted_records after JSON conversion.
Flush metrics and counts
plugins/out_stackdriver/stackdriver.c
Introduce formatted_records, skipped_records, failed_records, successful_records; handle formatted_records == 0 as no-retry and update cmt_dropped_records; clamp computed counts for metrics and partial-success handling.
Test payloads: invalid labels
tests/runtime/data/stackdriver/stackdriver_test_labels.h
Add macros for payloads where logging.googleapis.com/labels or custom labels key is not a map, including single-invalid, batch-mixed, and all-invalid variants.
Runtime tests: stackdriver behavior
tests/runtime/out_stackdriver.c
Add test callbacks and TEST_LIST entries asserting dropped records for non-map labels, mixed-batch behavior, first-record-dropped scenarios, and custom-label-key cases.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related issues

  • Issue #11541: Implements the centralized per-record skipping and prescan/metrics adjustments described in the roadmap issue.

Suggested labels

docs-required

Suggested reviewers

  • braydonk

Poem

🐇 I hop through records, nose to each byte,
Bad insertIds and scrambled labels—out of sight.
I drop the sour ones, keep the rest in play,
Tests stack like carrots, guiding my way,
A tidy trail of logs—hoppity hooray! 🥕

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 4.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'out_stackdriver: fix batch drop on invalid labels' directly summarizes the main change: fixing the issue where invalid labels in a single record caused the entire batch to be dropped.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@erain
erain force-pushed the fix/stackdriver-record-drop branch 3 times, most recently from fcf50e3 to 7928a60 Compare March 11, 2026 20:54
@erain
erain marked this pull request as ready for review March 11, 2026 22:02
@erain
erain requested a review from braydonk as a code owner March 11, 2026 22:02

@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.

Actionable comments posted: 1

Caution

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

⚠️ Outside diff range comments (1)
plugins/out_stackdriver/stackdriver.c (1)

1927-1929: ⚠️ Potential issue | 🟡 Minor

Log a summary reason before returning on all-invalid batches.

If prescan rejects every record, the function returns before the logged pass, so there is no message explaining that all entries were skipped for invalid insertId or labels. Emitting one summary warning here would make this failure mode diagnosable.

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

In `@plugins/out_stackdriver/stackdriver.c` around lines 1927 - 1929, When prescan
leaves no valid records (array_size == 0) add a summary warning log immediately
before the early return to explain why the batch is dropped (e.g., "all entries
skipped: invalid insertId or labels"); use the same logging facility already
used in this file (the local logger used elsewhere in stackdriver.c) and
reference the prescan result/array_size check so the message is emitted whenever
array_size == 0 instead of returning silently.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@plugins/out_stackdriver/stackdriver.c`:
- Around line 1917-1923: The loop that decrements array_size when
should_skip_record() returns true adjusts the emitted count locally but
cb_stackdriver_flush() still reports event_chunk->total_events; update the code
to propagate the actual emitted/processed count to cb_stackdriver_flush() (e.g.,
compute emitted_count by starting from event_chunk->total_events and
decrementing for each skipped record inside the
flb_log_event_decoder_next()/should_skip_record() loop or accumulate
emitted_count directly) and change metric reporting in cb_stackdriver_flush() to
use this emitted_count (or a new parameter name you add) instead of
event_chunk->total_events so dropped records are reflected in flush and
dropped-record metrics.

---

Outside diff comments:
In `@plugins/out_stackdriver/stackdriver.c`:
- Around line 1927-1929: When prescan leaves no valid records (array_size == 0)
add a summary warning log immediately before the early return to explain why the
batch is dropped (e.g., "all entries skipped: invalid insertId or labels"); use
the same logging facility already used in this file (the local logger used
elsewhere in stackdriver.c) and reference the prescan result/array_size check so
the message is emitted whenever array_size == 0 instead of returning silently.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 8d8e76ba-c3cc-4894-b540-33bba85548b2

📥 Commits

Reviewing files that changed from the base of the PR and between a1d9c2a and 7928a60.

⛔ Files ignored due to path filters (4)
  • tests/runtime/data/stackdriver/stackdriver_batch_all_labels_not_a_map.log is excluded by !**/*.log
  • tests/runtime/data/stackdriver/stackdriver_batch_first_record_labels_not_a_map.log is excluded by !**/*.log
  • tests/runtime/data/stackdriver/stackdriver_batch_labels_not_a_map.log is excluded by !**/*.log
  • tests/runtime/data/stackdriver/stackdriver_batch_mixed_errors.log is excluded by !**/*.log
📒 Files selected for processing (3)
  • plugins/out_stackdriver/stackdriver.c
  • tests/runtime/data/stackdriver/stackdriver_test_labels.h
  • tests/runtime/out_stackdriver.c

Comment thread plugins/out_stackdriver/stackdriver.c
erain added 2 commits June 9, 2026 15:28
When a single record contains a logging.googleapis.com/labels field that
is not a map, Stackdriver can drop the entire batch. This causes data
loss for valid records in the same batch.

Extract a shared should_skip_record() helper that validates insertId and
labels fields. Use it in the prescan loop and at the top of the main
packing loop so invalid records are skipped before field extraction.

Report the number of formatted records back to the flush path so
Stackdriver processed, retry, and dropped-record metrics use the records
actually sent. Treat an all-invalid batch as locally dropped instead of
a retryable formatter failure.

This change leaves existing batch-level errors unchanged, including
decoder initialization, k8s local_resource_id processing, and JSON
serialization failures.

Signed-off-by: Yu Yi <yiyu@yiyu.me>
Add Stackdriver runtime formatter coverage for invalid labels in both
single-record and batch flows.

Cover default labels, custom labels_key, records with fields that used
to require cleanup, mixed invalid insertId and labels, first-record
skips, and all-invalid batches.

Signed-off-by: Yu Yi <yiyu@yiyu.me>
@erain

erain commented Jun 9, 2026

Copy link
Copy Markdown
Contributor Author

This PR is ready for review. It has been rebased onto current master (including the recent stackdriver memory-leak fixes and grouped-log counter parity changes) and the full flb-rt-out_stackdriver runtime suite passes locally after the rebase.

Note on the 3 failing checks — all are unrelated to this change:

  • run-ubuntu-unit-tests (-DSANITIZE_UNDEFINED=On): fails on flb-rt-out_chronicle (format_multiple_records expects two records in a single flush but they were split across two flushes — a timing flake). flb-rt-out_stackdriver passed in this same job. Master shows the same intermittent unit-test failures (4 of the last 8 runs on master).
  • Unit tests (matrix): aggregator job that exits 1 when any matrix leg fails; red only because of the chronicle flake above.
  • pr-windows-build (Windows Arm64): luajit minilua failed to build under the MSVC Arm64 cross-compile toolchain — the build never reached this PR's code.

This PR only touches plugins/out_stackdriver/stackdriver.c and its runtime tests. Happy to address any review feedback.

msgpack_object *payload_labels_ptr;

/* Check insertId */
in_status = validate_insert_id(&insert_id_obj, obj);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

A Performance Optimization:

This iteration over the msgpack content (against the log entry) already calls validate_insert_id and get_payload_labels to traverse the list twice. Can you update the function to return the result of validate_insert_id and get_payload_labels as well?

With the change, we don't need to call the function in stackdriver_format again.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Good call — done in f6d50ee. should_skip_record() now returns the validated insertId (status + object) and the payload-labels pointer through out-params, and stackdriver_format() reuses them in the packing loop instead of calling validate_insert_id() and get_payload_labels() a second time per record. The prescan pass calls it with NULL out-params. Full flb-rt-out_stackdriver suite passes locally.

should_skip_record() already validates insertId and payload labels by
traversing the record. Return those results (insertId status and object,
payload labels pointer) so stackdriver_format() reuses them instead of
calling validate_insert_id() and get_payload_labels() a second time per
record in the packing loop.

Addresses review feedback on the double traversal.

Signed-off-by: Yu Yi <yiyu@google.com>
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.

2 participants