Skip to content

Stop the segment preprocess from building a Schema per segment - #19486

Open
xiangfu0 wants to merge 2 commits into
xiangfu0/data-3221-9-column-arraysfrom
xiangfu0/data-3221-10-physical-column-names
Open

Stop the segment preprocess from building a Schema per segment#19486
xiangfu0 wants to merge 2 commits into
xiangfu0/data-3221-9-column-arraysfrom
xiangfu0/data-3221-10-physical-column-names

Conversation

@xiangfu0

@xiangfu0 xiangfu0 commented Sep 6, 2026

Copy link
Copy Markdown
Contributor

What

Two things the segment preprocess does on every load asked the segment metadata for its Schema. The schema is derived and then cached, so each one pinned a Schema per loaded segment for the segment's whole life:

  • ForwardIndexHandler#computeOperations wants the set of physical column names.
  • ColumnMinMaxValueGenerator wants the columns its mode selects — the default mode is ALL, so this runs on every load too.

Both questions are answered by the column metadata the schema is itself derived from. SegmentMetadata#getPhysicalColumnNames() walks the column metadata (falling back to the schema for a segment that holds no column metadata, i.e. a CONSUMING one), and the min/max generator selects straight off each column's field spec instead of off schema.getAllFieldSpecs().

No behavior change: the same columns are selected, in the same order the column metadata is held in, and getSchema() still returns the same schema for anyone who asks for it.

Why

Found on a production server holding 13.6k loaded segments: one Schema per segment, ~144 MB of tree entries and list slots, all of it a second copy of what the column metadata already holds. Part 7 of this series (#19478) made the per-segment schema lazy; these two callers were the reason it was still built for every segment anyway.

Tests

SegmentMetadataImplTest#testPreprocessDoesNotBuildTheSegmentSchema: getPhysicalColumnNames() returns what getSchema().getPhysicalColumnNames() returns without materializing a schema, and a full SegmentPreProcessor#process() over a v3 segment materializes none. The test fails if either call site is reverted.

Stack

Part 10, based on #19481. Review only this part's own commit; the earlier parts account for the rest of the diff.

  1. Allocate ColumnMetadataImpl index sizes lazily and skip the index_map lookup without an index dir #19480 lazy index-size storage
  2. Canonicalize the default null value and intern per-column strings at metadata parse time #19473 canonical default-null values and interned per-column strings
  3. Delegate immutable DataSourceMetadata to ColumnMetadata instead of snapshotting it #19474 delegating immutable DataSourceMetadata
  4. Fold PhysicalColumnIndexContainer's IndexTypeMap into a presence mask and a dense reader array #19475 presence-mask index container
  5. Share segment-derived FieldSpec instances across segments through a weak interner #19476 weak FieldSpec interner
  6. Materialize immutable-segment columns lazily behind an opt-in instance config (default off) #19477 opt-in lazy column materialization
  7. Slim ColumnMetadataImpl to 72 bytes and derive the per-segment Schema lazily #19478 slim ColumnMetadataImpl and lazy per-segment Schema
  8. Store numeric column min/max as primitives instead of boxed Comparables #19479 primitive numeric min/max
  9. Hold segment column metadata in sorted arrays and derive the map on demand #19481 sorted-array column metadata store
  10. this PR — stop the preprocess from building a schema per segment

🤖 Generated with Claude Code

@xiangfu0 xiangfu0 added performance Related to performance optimization memory Related to memory usage or optimization labels Sep 6, 2026
@codecov-commenter

codecov-commenter commented Sep 7, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 89.28571% with 3 lines in your changes missing coverage. Please review.
✅ Project coverage is 67.79%. Comparing base (f0a20da) to head (1bad6fd).

Files with missing lines Patch % Lines
.../columnminmaxvalue/ColumnMinMaxValueGenerator.java 80.00% 1 Missing and 2 partials ⚠️
Additional details and impacted files
@@                           Coverage Diff                            @@
##             xiangfu0/data-3221-9-column-arrays   #19486      +/-   ##
========================================================================
+ Coverage                                 67.76%   67.79%   +0.02%     
  Complexity                                 1430     1430              
========================================================================
  Files                                      3493     3493              
  Lines                                    225070   225067       -3     
  Branches                                  35550    35545       -5     
========================================================================
+ Hits                                     152515   152578      +63     
+ Misses                                    60517    60450      -67     
- Partials                                  12038    12039       +1     
Flag Coverage Δ
integration 100.00% <ø> (ø)
integration1 100.00% <ø> (ø)
integration2 0.00% <ø> (ø)
java-25 67.79% <89.28%> (+0.02%) ⬆️
lane-a 100.00% <ø> (ø)
lane-b 0.00% <ø> (ø)
temurin 67.79% <89.28%> (+0.02%) ⬆️
unittests 67.78% <89.28%> (+0.02%) ⬆️
unittests1 57.80% <78.57%> (+0.01%) ⬆️
unittests2 39.47% <46.42%> (+0.02%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

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

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

xiangfu0 and others added 2 commits September 8, 2026 18:49
…hema

Two things the preprocess does on every segment load asked the segment
metadata for its schema, and since the schema is derived and then cached,
each one pinned a per-segment `Schema` for the segment's whole life:

- `ForwardIndexHandler#computeOperations` needs the set of physical column
  names, and
- `ColumnMinMaxValueGenerator` needs the columns its mode selects (the
  default mode is `ALL`, so this runs on every load).

Both questions are answered by the column metadata the schema is itself
derived from. `SegmentMetadata#getPhysicalColumnNames()` walks the column
metadata for the first, falling back to the schema for a segment that holds
no column metadata (a CONSUMING one), and the min/max generator now selects
straight off each column's field spec.

On a server measured with 13.6k loaded segments, the schemas built here were
~144 MB of tree entries and list slots, all of it a second copy of data the
column metadata already holds.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Two follow-ups on `SegmentMetadata#getPhysicalColumnNames()`, both found by
review of the previous commit rather than by a failure:

- It returned a `HashSet` for a segment that holds column metadata but the
  schema's `TreeSet` for one that does not, so the expression it replaced
  (`getSchema().getPhysicalColumnNames()`, always sorted) silently became
  unordered for immutable segments and stayed sorted for CONSUMING ones —
  the worst shape for a caller that assumes order. It now returns a
  `SortedSet` for both, as the replaced expression did.

- Its schema fallback for a segment holding no column metadata was reached
  through the default `getAllColumnMetadata()`, which is
  `getColumnMetadataMap().values()` — and `getColumnMetadataMap()` is
  `@Nullable`, documented to answer `null` for exactly that segment. The
  fallback worked only because the one implementation in the tree overrides
  the accessor. That default now honors its own documented contract and
  answers empty for a null map.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@xiangfu0
xiangfu0 force-pushed the xiangfu0/data-3221-10-physical-column-names branch from 4a3d0d6 to 1bad6fd Compare September 9, 2026 01:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

memory Related to memory usage or optimization performance Related to performance optimization

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants