Skip to content

fix(buffers): drop over-limit disk_v2 records instead of crashing#25795

Open
graphcareful wants to merge 7 commits into
vectordotdev:masterfrom
graphcareful:fix/disk-buffer-drop-oversized-records
Open

fix(buffers): drop over-limit disk_v2 records instead of crashing#25795
graphcareful wants to merge 7 commits into
vectordotdev:masterfrom
graphcareful:fix/disk-buffer-drop-oversized-records

Conversation

@graphcareful

Copy link
Copy Markdown
Contributor

Summary

A record whose encoded size exceeds the disk_v2 buffer's maximum record size caused the writer to return an unrecoverable error, which tore down the entire Vector topology.

This fix handles the too-large case inside the writer:

  • Extracts the record's finalizers before encoding (encoding consumes the record)
  • Rejects the finalizers with a terminal status so upstream sources stop retrying a record that can never succeed
  • Logs at error level
  • Meters the drop via buffer_discarded_events_total / buffer_discarded_bytes_total (intentional=false)
  • Returns Ok(0) — the buffer and all other records are unaffected

Genuinely fatal errors (I/O, serialization, inconsistent state) still propagate.

To enable finalizer extraction before encoding, Finalizable is added as a supertrait of InMemoryBufferable (it belongs alongside AddBatchNotifier), with trivial impls for the handful of test types that didn't already have it.

Vector configuration

No configuration changes. The fix is internal to the disk_v2 buffer writer.

How did you test this PR?

  • Unit tests in size_limits.rs assert the writer drops oversized records, rejects their finalizers, and increments buffer_discarded_events_total/buffer_discarded_bytes_total, while the buffer continues accepting subsequent records.
  • Discovered and confirmed via Antithesis fault-injection testing.

Change Type

  • Bug fix
  • New feature
  • Dependencies
  • Non-functional (chore, refactoring, docs)
  • Performance

Is this a breaking change?

  • Yes
  • No

Does this PR include user facing changes?

  • Yes. Please add a changelog fragment based on our guidelines.

A changelog fragment is included: changelog.d/disk_v2_drop_oversized_record_instead_of_crashing.fix.md

References

@graphcareful graphcareful requested a review from a team as a code owner July 9, 2026 19:28
@github-actions github-actions Bot added the domain: core Anything related to core crates i.e. vector-core, core-common, etc label Jul 9, 2026

@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: 66d8721cfd

ℹ️ About Codex in GitHub

Codex has been enabled to automatically 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 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread lib/vector-buffers/src/variants/disk_v2/writer.rs Outdated
Comment thread lib/vector-buffers/src/variants/disk_v2/ledger.rs Outdated
Comment thread lib/vector-buffers/src/variants/disk_v2/writer.rs
@graphcareful graphcareful requested a review from blt July 9, 2026 19:40
@datadog-vectordotdev

This comment has been minimized.

@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: ca703b4a76

ℹ️ About Codex in GitHub

Codex has been enabled to automatically 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 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread lib/vector-buffers/src/variants/disk_v2/tests/size_limits.rs Outdated
Comment thread lib/vector-buffers/src/variants/disk_v2/writer.rs Outdated
@graphcareful graphcareful marked this pull request as draft July 9, 2026 21:21
A record whose encoded size exceeds the disk_v2 max record size made the writer
return an unrecoverable error, which tore down the entire Vector topology.

Handle the too-large class (RecordTooLarge / FailedToEncode) inside the writer:
extract finalizers before encoding consumes the record, reject them on failure
(terminal status so the source stops retrying a record that can never succeed),
log at error, meter the drop via buffer_discarded_events_total/bytes_total
(intentional=false), and return Ok(0). The buffer and every other record are
untouched. Genuinely fatal errors (I/O, serialization, inconsistent state) still
propagate.

To enable the finalizer extraction, Finalizable is added as a supertrait of
InMemoryBufferable (it belongs alongside AddBatchNotifier) with trivial impls
for the handful of test types that didn't already have it.
Acking sources map BatchStatus::Rejected back to a nack or withheld
checkpoint (Pulsar: nack_with_id; file source: only checkpoints Delivered).
Using Rejected on an oversized record therefore turns the drop into a
poison-pill retry loop instead of discarding it permanently.

Drop the finalizers with their default EventStatus::Dropped, which
propagates as BatchStatus::Delivered, so sources ack/checkpoint and
move on.
- Resolve finalizers as Delivered (not Rejected) so acking sources
  ack/checkpoint rather than nacking a record that can never be written
- Track pre-entry drops in a separate counter that does not feed into
  total_left, so buffer occupancy is not corrupted by records that never
  entered the buffer
… retry

take_finalizers runs before archive_record because the encoder
unconditionally consumes the record. If can_write_record is false the
record is recovered and returned to the caller, but record_finalizers
was dropped as Delivered, acking the upstream source before the record
entered any buffer.

Add merge_finalizers to Finalizable (no-op default) and implement it
for Event/LogEvent/Metric/TraceEvent/EventArray/SourceSenderItem and
the buffer test-message types. Call it before returning the recovered
record so finalizers travel with the record through block/overflow.
…er-record

The sanity-check in `archive_record` (pos <= 8 or buf.len() != pos) detects
broken serializer state — a writer-level invariant violation — not an oversized
or malformed record. Returning `FailedToSerialize` here caused `is_unwritable_record`
to match it, silently dropping every affected record as Delivered and letting
the writer continue with a broken serializer. All subsequent records hit the
same state and are also silently dropped.

Fix: return `InconsistentState` from that branch so the error propagates as
fatal, logging "Disk buffer writer has encountered an unrecoverable error" and
shutting down the topology rather than masking data loss.

Also remove `FailedToSerialize` from `is_unwritable_record` entirely. Its
remaining source is the OOM scratch-space path, which is a transient, system-
wide condition — not a permanent per-record fault. Silently dropping records as
Delivered under OOM is worse than crashing and restarting.
@graphcareful graphcareful force-pushed the fix/disk-buffer-drop-oversized-records branch from ca703b4 to a848605 Compare July 10, 2026 15:32
BufferSender previously incremented the sender-layer received usage before attempting the primary write. That made DropNewest full writes easy to balance with a dropped metric, but it also counted Overflow sends against the primary buffer even when the primary write returned Full and the item was forwarded to the overflow buffer instead.

Move sender usage accounting to follow TryWriteOutcome. Written records are counted as accepted, DropNewest full records are counted as received and intentionally dropped, and writer-level Dropped outcomes are left unmetered here because the disk writer already records the pre-entry drop and ledger adjustment.

This keeps occupancy balanced when overflow forwarding succeeds without reintroducing false sender-layer drop metrics, and adds coverage for the overflow-block path.
@graphcareful graphcareful force-pushed the fix/disk-buffer-drop-oversized-records branch from a848605 to 3d11b3f Compare July 10, 2026 16:56
@graphcareful graphcareful marked this pull request as ready for review July 10, 2026 16:56
@graphcareful

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Delightful!

Reviewed commit: 3d11b3f86b

ℹ️ About Codex in GitHub

Codex has been enabled to automatically 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 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

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

Labels

domain: core Anything related to core crates i.e. vector-core, core-common, etc

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant