Skip to content

fix: don't re-copy already emitted strings in Utf8View group-by emit - #27360

Open
Kristofferjoha wants to merge 5 commits into
apache:mainfrom
Kristofferjoha:fix-byte-view-take-n-copy
Open

Kristofferjoha wants to merge 5 commits into
apache:mainfrom
Kristofferjoha:fix-byte-view-take-n-copy

Conversation

@Kristofferjoha

Copy link
Copy Markdown
Contributor

Which issue does this PR close?

Rationale for this change

A streaming GROUP BY with a Utf8View column among several group columns emits small batches that carry far more string data than their rows use. The cause is in ByteViewGroupValueBuilder::take_n. On each partial emit it copies the in-progress string buffer from byte 0 up to the last emitted string, and never trims that buffer. So every emitted batch carries the strings of all earlier batches from the same block.

What changes are included in this PR?

After a partial take from the in-progress buffer, drain the taken bytes from it and shift the offsets of the remaining views. Each emitted batch then holds only its own strings. drain moves the not-yet-emitted bytes to the front of the buffer on each partial emit. In the streaming case that is only a few groups.

Not changed: when the last emitted string is in a completed block, the emitted batch still shares that whole block (up to 2 MB). In the streaming case that should now be rare, since the in-progress buffer only holds groups not yet emitted.

What is the testing strategy for this PR?

New unit test test_byte_view_take_n_drains_in_progress. It takes one row at a time and checks that each emitted array's data buffer holds only its own string, that the in-progress buffer shrinks, and that the remaining and newly appended values are still correct.

I also ran the repro from the issue with the cli, and compared to main:

Memory limit main This PR
-m 200M ResourcesExhausted passes
-m 50M ResourcesExhausted passes
-m 20M ResourcesExhausted passes
-m 10M ResourcesExhausted passes

EXPLAIN ANALYZE of the same query without a memory limit:

Operator main output_bytes This PR output_bytes
AggregateExec (Partial) 42.6 MB 1023.1 KB
RepartitionExec 330.0 MB 5.5 MB

Are there any user-facing changes?

No

@Kristofferjoha

Copy link
Copy Markdown
Contributor Author

run benchmarks

@adriangbot

Copy link
Copy Markdown

@Kristofferjoha

Copy link
Copy Markdown
Contributor Author

@adriangb this fixes the aggregate part of #27323 . Could you run the benchmarks on it?

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

Two logic questions; no code suggestions.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

The offset shift is subtracted from every non-inlined view. Does that rely on all remaining views pointing to the in-progress buffer, and is the mixed completed/in-progress case tested? Also, is drain O(n^2) when many groups remain across repeated partial emits?

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.

Added the test. As for the drain, it shouldnt really matter, as it is capped at a specific block size. Should stay linear. But i want the benchmark ran to make sure

@alamb

alamb commented Nov 12, 2026

Copy link
Copy Markdown
Contributor

Cna you pleaes provide an end to end test / reproducer of what you are trying to do (rather than code level description / unit test)?

@adriangb

Copy link
Copy Markdown
Contributor

@Kristofferjoha the issue report includes a minimal reproducible example you should be able to work into an SLT test

@github-actions github-actions Bot added the sqllogictest SQL Logic Tests (.slt) label Nov 13, 2026
@Kristofferjoha

Copy link
Copy Markdown
Contributor Author

@alamb @adriangb Added the reproducer from #27323 as an SLT. It runs GROUP BY k, s on a 5.6 MB parquet file sorted by k, where s is Utf8View. On main it fails with ResourcesExhausted at a 200M memory limit, even though the result is only 7000 rows. With this PR it passes at 200M, 50M and 20M.

Comment on lines +15 to +20
query I
COPY (
SELECT value % 1000 AS k, repeat('x', 100) || (value % 7)::varchar AS s, value AS v
FROM generate_series(1, 2000000)
ORDER BY k
)

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.

How long does this test take to run? Can we reduce the data sizes and still reproduce?

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

Labels

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.

5 participants