Skip to content

Fix GroupValues retained memory accounting - #25188

Draft
kosiew wants to merge 9 commits into
apache:mainfrom
kosiew:memcalc-11-23393
Draft

Fix GroupValues retained memory accounting#25188
kosiew wants to merge 9 commits into
apache:mainfrom
kosiew:memcalc-11-23393

Conversation

@kosiew

@kosiew kosiew commented Sep 11, 2026

Copy link
Copy Markdown
Contributor

Which issue does this PR close?

Rationale for this change

GroupValues::size() can underreport memory retained by group-value state. In particular, the owner descriptors are not consistently included, and the column-based implementation does not account for several retained allocations used for collision handling, emit scratch space, and vectorized operations.

This can make aggregate memory accounting lower than the memory actually retained by GroupValues, including after reusable buffers have been cleared or groups have been emitted.

This PR updates the accounting so each GroupValues implementation reports its owner descriptor and owned retained allocations exactly once, using allocation capacity where appropriate.

What changes are included in this PR?

This PR:

  • Includes the owner descriptor in GroupValuesPrimitive, GroupValuesRows, and GroupValuesColumn memory accounting.
  • Includes concrete boxed GroupColumn owner descriptors in their size() implementations.
  • Updates row-backed accounting to avoid double-counting inline Rows and RowConverter descriptors.
  • Accounts for the GroupValuesColumn allocation backing its group-column collection.
  • Accounts for retained group-index lists, including their nested allocations.
  • Accounts for the emit group-index scratch buffer.
  • Adds accounting for vectorized-operation buffers, including retained capacity after they are cleared.
  • Continues to account for reusable buffers by capacity rather than logical length.
  • Adjusts two aggregate spill-test memory limits to accommodate the corrected memory accounting.

Are these changes tested?

Yes. This PR adds focused regression tests covering:

  • Empty and grown GroupValuesPrimitive memory accounting.
  • Empty and grown GroupValuesRows memory accounting.
  • Retained row and hash buffers across emit and subsequent reuse.
  • Boxed primitive and row-backed GroupColumn owner descriptors.
  • Collision group-index lists, emit scratch storage, and vectorized-operation buffers in GroupValuesColumn.
  • Retained vectorized and emit scratch capacity after buffers are cleared.

The tests compute expected sizes from the relevant owner descriptors and retained allocations rather than relying on global allocator measurements.

Are there any user-facing changes?

There are no API changes.

Aggregate memory accounting is more complete, so workloads using these GroupValues implementations may report or reserve more memory than before. This can affect when memory limits cause aggregation to spill, reflecting memory that was already retained but previously omitted from GroupValues::size().

LLM-generated code disclosure

This PR includes LLM-generated code and comments. All LLM-generated content has been manually reviewed.

- GroupValues::size() charges owner + retained buffers.
- Column: added group-index, emit, vectorized, and vec backing.
- Added capacity/reuse tests.
- Updated tight spill test pools.
…) calls

- Updated `row.rs` to subtract inline `RowConverter`/`Rows` descriptors when calculating nested `.size()` calls.
- The outer `GroupValuesRows` descriptor is now charged only once, preventing duplicate size accounting.
- This resolves overcounting of size for nested rows, improving the accuracy of size calculations.
- No functional changes to the row data itself; only the size accounting logic has been refined.
- Enhances performance and reliability for operations that rely on precise size metrics.
- Added a dedicated integration test in `datafusion/physical-plan/src/aggregates/group_values/multi_group_by/mod.rs`
- The test validates scratch‑capacity and reuse behavior across:
- Five distinct vectorized buffers
- The `emit_scratch` logic path
- Verifies that buffer growth (delta) correctly allocates additional capacity
- Confirms that clearing the buffers retains the allocated charge, ensuring proper reuse without unnecessary reallocations
…pe row‑backed inline nested descriptors, add primitive row‑backed multi‑column tests, and enable 1,024 spill limit

- All missing GroupColumn owner descriptors charged.
- Row‑backed inline nested descriptors deduped.
- Added primitive, row‑backed, multi‑column tests.
- Existing 1,024 spill limit now spills; ordered test passes.
- Added `size_retains_reusable_buffers_after_emit` test to verify reusable buffers are retained after emit.
- Checks `rows_buffer` and hash scratch retention after `EmitTo::First`.
- Re‑interns values and rechecks accounting to ensure correct behavior after re‑emission.
- Moved comment to correct emit test.
@github-actions github-actions Bot added the physical-plan Changes to the physical-plan crate label Sep 11, 2026
- Increase SLT peak from **9.2 KB** to **9.4 KB**.
- Retain spill test plan while adding assertions for **spill count** and **spill bytes**.
- Update pool sizes: **non‑distinct 1,000,000** entries and **DISTINCT 4,256,000** entries.
- Revised overall plan to reflect the metric adjustments and new test assertions.
@github-actions github-actions Bot added core Core DataFusion crate sqllogictest SQL Logic Tests (.slt) labels Sep 11, 2026
…TINCT pool

- Modified the partial‑aggregation logic to skip only when memory limits are exceeded, rather than under broader conditions.
- Reduced the DISTINCT pool size from `4_256_000` to `1_000_000` to lower memory consumption and improve performance in constrained environments.
@codecov-commenter

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 94.51477% with 13 lines in your changes missing coverage. Please review.
✅ Project coverage is 81.94%. Comparing base (6263df2) to head (a57639a).
⚠️ Report is 9 commits behind head on main.

Files with missing lines Patch % Lines
.../src/aggregates/group_values/multi_group_by/mod.rs 95.58% 0 Missing and 6 partials ⚠️
...n/physical-plan/src/aggregates/group_values/row.rs 89.28% 0 Missing and 6 partials ⚠️
...gregates/group_values/single_group_by/primitive.rs 93.75% 0 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main   #25188      +/-   ##
==========================================
+ Coverage   81.91%   81.94%   +0.02%     
==========================================
  Files        1132     1133       +1     
  Lines      421280   423753    +2473     
  Branches   421280   423753    +2473     
==========================================
+ Hits       345111   347248    +2137     
- Misses      55767    55900     +133     
- Partials    20402    20605     +203     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

…llability spill test

- Updated `nested_nullability.rs` test budget from `1_000_000` to `4_256_000`.
- Identified cause: DISTINCT struct state experiences a transient peak that exhausts the 1 MiB fair pool during spilling.
- Adjusted the budget to accommodate the peak memory usage, ensuring the test passes reliably.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

core Core DataFusion crate physical-plan Changes to the physical-plan crate sqllogictest SQL Logic Tests (.slt)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants