Skip to content

input_chunk: keep log groups whole across appends - #12595

Open
edsiper wants to merge 5 commits into
masterfrom
fix/input-chunk-open-groups
Open

edsiper wants to merge 5 commits into
masterfrom
fix/input-chunk-open-groups

Conversation

@edsiper

@edsiper edsiper commented Oct 17, 2026 •

Copy link
Copy Markdown
Member

Problem

A log group (a group start marker, its records and the group end marker, for
example one OpenTelemetry resource and scope) must reach outputs whole. If a
group is split across chunks, the output that flushes the second chunk gets
records and an end marker without the start. OTLP outputs then export those
records without their resource and scope.

Groups were split in these cases:

  1. Flush between appends. When a sender splits a group across forward
    messages, the start and the end arrive in separate appends. A flush in
    between turned the chunk into a task, and the rest of the group went into a
    new chunk. The same happened with in_forward Message mode, where every
    event is its own append.
  2. Chunk size limit. A chunk is closed after an append that takes it past
    2 MB, even in the middle of a group, so the next append of the group went
    into a new chunk.
  3. in_forward Forward mode batching. A frame larger than the ingress byte
    limit (in practice workers > 1 with mem_buf_limit set, and a frame
    larger than mem_buf_limit) was split into batches by size. Two problems:
    • the batch decoder skipped group markers without counting their bytes,
      so a batch could cut off the end of its last record and write invalid
      MessagePack into the chunk, and markers were dropped;
    • a split could fall inside a group.
  4. Conditional routing. When an append left a group open, the route payload
    repeated the group start before every matching record. The end of a group
    started by a previous append was only kept by accident, when no other
    group followed it in the append.

The in-tree inputs that create groups (opentelemetry, and rewrite_tag through
emitter) always append complete groups. I found no other input that leaves a
group open.

Changes

  • input_chunk: track whether a log chunk ends inside a group, from the
    markers of each append, and keep such a chunk open until the group ends:
    • it is not closed for size until twice the size limit;
    • flb_input_chunk_holds_open_group() tells the dispatcher to wait;
    • the wait is bounded to 5 seconds, so a sender that never ends a group
      does not hold its records, and it ends on shutdown;
    • a byte scan for possible marker headers skips buffers without markers,
      so inputs that do not create groups pay a memchr pass and nothing
      else.
  • engine_dispatch: skip chunks that are waiting for the end of a group.
  • in_forward: read group markers as events when splitting a large frame,
    so every batch is exact, and split only between groups: when a group does
    not fit, the events before it are ingested and the group starts the next
    batch. A single group larger than the limit is rejected, as a single record
    larger than the limit already is.
  • input_log: emit the start of a routed group once, and emit the end of a
    continued group after its records.

Compatibility

  • No configuration or format changes.
  • A chunk that ends inside a group is flushed up to 5 seconds later than
    before, and only until the group ends. Chunks without open groups are
    unaffected.
  • A chunk holding an open group can grow up to 4 MB instead of 2 MB.

Testing

New integration tests in tests/integration/scenarios/in_forward. All fail
on 5.1.0 and pass with this change.

  • test_in_forward_group_split_across_messages_stays_in_one_chunk: a flush
    between the start and the end of a group;
  • test_in_forward_group_crossing_the_chunk_size_stays_in_one_chunk: a group
    crossing the 2 MB chunk limit;
  • test_in_forward_open_group_is_flushed_after_the_hold_limit: a group that
    never ends is released after the hold limit;
  • test_in_forward_workers_split_large_frames_between_groups: a 600 KB frame
    with six groups against a 128 KB ingress limit is delivered intact, with
    every group whole;
  • test_in_forward_conditional_route_keeps_a_split_group_whole: a routed
    group split across appends, followed by a complete group.

Linux x86_64 debug build:

ctest --test-dir build -j8
pytest scenarios -n 8
VALGRIND=1 VALGRIND_STRICT=1 pytest scenarios/in_forward scenarios/in_opentelemetry \
    scenarios/out_forward scenarios/out_opentelemetry scenarios/out_stdout \
    scenarios/in_storage_backlog scenarios/storage -n 8
  • ctest: 213 of 215 pass. flb-rt-out_http is the flaky test that also
    fails on master. flb-rt-in_forward binds port 24224, which another
    process holds on the test machine.
  • Integration suite: 804 passed. The 40 failures are environmental and also
    happen on master: the Kafka schema registry tests need the protobuf
    encoder, unix_socket_decode_status needs the unix_socket input, and the
    systemd collector tests.
  • Valgrind (VALGRIND_STRICT=1) on the scenarios listed above: 369 Fluent Bit
    runs, no errors and no definite leaks.
  • Soak: OTLP logs, metrics and traces plus forward logs whose groups are split
    across two messages, restarts every 60 seconds, 2% of exports rejected with
    503. 1,181 split groups were all delivered with their resource, with no
    missing and no duplicated records. Without this change, between 2% and 3% of
    the forward records were exported without their resource.

Summary by CodeRabbit

  • New Features
    • Forward input now keeps grouped log events together when messages span multiple appends or exceed normal chunk-size limits.
    • Large batches are split between complete groups rather than within a group; groups too large to fit are kept intact.
    • Open groups are held briefly to allow them to complete, then released for processing.
    • Conditional routing preserves group boundaries when matching records arrive across separate appends.

A group whose start and end arrive in separate appends, such as forward
messages from a sender that splits a group, was split across chunks when
the chunk was flushed or closed for size between the appends. Outputs then
received the end of the group without its start and lost its context.

Track whether a log chunk ends inside a group, from the group markers of
each append, and keep such a chunk open until the group ends: it is not
closed when it grows past its size limit, up to twice that limit, and
flb_input_chunk_holds_open_group() tells the dispatcher to wait for it.
The wait is bounded to five seconds and ends on shutdown. A cheap byte
scan skips buffers that cannot contain markers.

Signed-off-by: Eduardo Silva <eduardo@chronosphere.io>
Skip a chunk that still ends inside a log group, so the rest of the group
is appended to it instead of a new chunk.

Signed-off-by: Eduardo Silva <eduardo@chronosphere.io>
When an append left a group open, the route payload repeated its start
marker before every matching record, and the end of a group started by a
previous append was only kept by accident when no other group followed.
Emit the start of a group once, and emit the end of a continued group
after its records.

Signed-off-by: Eduardo Silva <eduardo@chronosphere.io>
A Forward mode frame larger than the ingress byte limit was split into
batches by size. The decoder skipped group markers without counting their
bytes, so a batch could cut the end of its last record and drop markers,
and a split could fall inside a group. Read the markers as events, and
when a group does not fit, ingest the events before it and start the next
batch with the group.

Signed-off-by: Eduardo Silva <eduardo@chronosphere.io>
Signed-off-by: Eduardo Silva <eduardo@chronosphere.io>
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 17, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-17T18:55:41.764592Z a4f6105 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@coderabbitai

coderabbitai Bot commented Oct 17, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: defaults
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: ca535c28-f8b4-4f71-a869-5164411d3229
📥 Commits

Reviewing files that changed from the base of the PR and between f5374f7 and a4f6105.

📒 Files selected for processing: 9
  • include/fluent-bit/flb_input_chunk.h
  • plugins/in_forward/fw_prot.c
  • src/flb_engine_dispatch.c
  • src/flb_input_chunk.c
  • src/flb_input_log.c
  • tests/integration/scenarios/in_forward/config/in_forward_conditional_groups_to_forward_receiver.yaml
  • tests/integration/scenarios/in_forward/config/in_forward_split_groups_to_forward_receiver.yaml
  • tests/integration/scenarios/in_forward/config/in_forward_workers_groups_to_forward_receiver.yaml
  • tests/integration/scenarios/in_forward/tests/test_in_forward_001.py

Included review availability: This review used your included allowance. Your plan provides up to 8 included reviews per hour; 7 remain after this review.

📝 Walkthrough

Forward input now preserves log group boundaries when splitting batches, appending records to chunks, delaying chunk dispatch, and building routed payloads. Integration tests cover groups split across messages, size limits, worker mode, timeouts, and conditional routes.

Changes: Forward group handling

Layer / File(s) Summary
Group-aware chunk state and dispatch
include/fluent-bit/flb_input_chunk.h, src/flb_input_chunk.c, src/flb_engine_dispatch.c
Chunks detect group markers and track whether a group remains open. An open group can keep a chunk unlocked beyond the normal filesystem size limit, up to a separate maximum. The dispatcher defers chunks held for an open group until the group closes or the hold conditions end.
Forward batch splitting at group boundaries
plugins/in_forward/fw_prot.c
The Forward batch splitter counts marker bytes and splits oversized batches at group boundaries. It keeps an oversized group intact and defaults unreadable record types to normal records.
Routed payload continuity and integration coverage
src/flb_input_log.c, tests/integration/scenarios/in_forward/config/in_forward_*groups_to_forward_receiver.yaml, tests/integration/scenarios/in_forward/tests/test_in_forward_001.py
The route and default-route payload builders preserve group markers when matching records continue from an earlier append. Integration tests check split groups, hold timing, chunk-size limits, worker-mode frames, and conditional routing.

Priority: ➖ Normal

Estimated code review effort: 3 (Moderate) | ~25 minutes

Change: Bug fix

Sequence Diagram(s)

sequenceDiagram
  participant ForwardSender
  participant fw_ingest_forward_mode_batch
  participant flb_input_chunk
  participant flb_engine_dispatch
  participant build_payload_for_route
  ForwardSender->>fw_ingest_forward_mode_batch: Send frame with grouped events
  fw_ingest_forward_mode_batch->>flb_input_chunk: Append batches split at group boundaries
  flb_engine_dispatch->>flb_input_chunk: Check whether the chunk holds an open group
  flb_input_chunk-->>flb_engine_dispatch: Return hold status
  flb_engine_dispatch->>build_payload_for_route: Build payload for an eligible chunk
Loading
Merge Risk: ⚪ Minimal · up to a4f61

The change keeps log groups together across appends and large Forward frames. No concrete merge-blocking problem was identified in the supplied material.

Pre-merge checks: Passed 4 · Failed 1

Failed Failed checks: 1 warning

Check name Status Explanation Resolution
Docstring Coverage Warning Docstring coverage is 46.15% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 26 functions across 6 files. (3 skipped: … Write docstrings for the functions missing them to satisfy the coverage threshold.
Passed Passed checks: 4 passed
Check name Status Explanation
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.
Description Check Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check Passed The title clearly and concisely describes the primary change: preserving log groups across input chunk appends.
Full details: Docstring Coverage

Explanation

Docstring coverage is 46.15% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 26 functions across 6 files. (3 skipped: 3 unsupported.)

Resolution

Write docstrings for the functions missing them to satisfy the coverage threshold.

  • Fix all pre-merge checks with AI
✨ Finishing Touches: 💡 2
📝 Generate docstrings: 💡
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
🛠️ Fix failing CI checks: 💡
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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.

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a4f6105a9e

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/flb_input_chunk.c
Comment on lines +2852 to +2855
if (p[1] != 0x92) {
p++;
continue;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Detect group markers in Forward-format records

When an input appends a valid FLB_LOG_EVENT_FORMAT_FORWARD record, the accepted layout is [timestamp, body], so an EventTime group marker starts with 0x92 0xd7..., not 0x92 0x92.... This check therefore makes log_buffer_may_have_groups() return false before the parser's existing legacy-header path can run; group_open remains false and a flush between appends can still split that group. Accept the scalar timestamp layout here as well as the Fluent Bit v2 nested header.

Useful? React with 👍 / 👎.

This branch was successfully deployed

1 active deployment
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant