Drop star-tree indexes left unreadable by a dimension encoding change - #19349
Drop star-tree indexes left unreadable by a dimension encoding change#19349gortiz wants to merge 2 commits into
Conversation
A star-tree stores its dimension values as dictionary ids in a fixed-bit forward index whose bit width is read from the main column metadata at load time. Moving a star-tree dimension column to `noDictionaryColumns` makes `ForwardIndexHandler` re-encode that column to raw, but with `enableDynamicStarTreeCreation` disabled `SegmentPreProcessor` leaves the star-tree untouched. The segment then becomes permanently unloadable: `StarTreeLoaderUtils` asks `FixedBitIntReader` for a reader over the now unavailable bit width and gets a bare `IllegalStateException`, so the segment goes to ERROR on every load, e.g. on any restart that reloads it. The re-encoding is persisted, so an affected segment is already inconsistent on disk and `needProcess()` no longer reports anything to do, which makes a plain reload fail in exactly the same way. - `SegmentPreProcessor` now removes star-trees whose dimension column lost its dictionary even when `enableDynamicStarTreeCreation` is disabled. Removing star-trees only deletes files, unlike the rebuild that flag guards against. When the flag is enabled the regular flow already rebuilds them, since the split order no longer matches the builder configs. - `StarTreeLoaderUtils` skips an unreadable star-tree with a warning instead of failing the whole segment load, covering segments whose pre-processing is skipped. - `FixedBitIntReader` now reports the offending number of bits. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## master #19349 +/- ##
============================================
+ Coverage 67.10% 67.13% +0.03%
Complexity 1424 1424
============================================
Files 3468 3468
Lines 222353 222378 +25
Branches 34999 35009 +10
============================================
+ Hits 149212 149304 +92
+ Misses 61224 61153 -71
- Partials 11917 11921 +4
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Jackie-Jiang
left a comment
There was a problem hiding this comment.
Thinking more of the problem, when a dictionary encoded column is removed, but star-tree is configured, we should keep the dictionary, just dropping the forward index. Star-tree can be used with RAW forward index and dictionary. See #19153
| public static String findUnloadableDimension(StarTreeV2Metadata starTreeMetadata, SegmentMetadata segmentMetadata) { | ||
| for (String dimension : starTreeMetadata.getDimensionsSplitOrder()) { | ||
| ColumnMetadata columnMetadata = segmentMetadata.getColumnMetadataFor(dimension); | ||
| if (columnMetadata == null || !columnMetadata.hasDictionary()) { |
There was a problem hiding this comment.
@deepthi912 Can you also take a look and see if this works well with the new added raw dimension support
| List<StarTreeV2> starTrees = segment.getStarTrees(); | ||
| assertNotNull(starTrees); | ||
| assertEquals(starTrees.size(), 1); | ||
| assertEquals(starTrees.get(0).getMetadata().getDimensionsSplitOrder(), List.of("intCol")); |
There was a problem hiding this comment.
#19153 added support for star-tree on raw encoded column with separate dictionary. We should verify if the preprocess can rebuild the star-tree to generate dictionary.
Address review feedback on apache#19349: - Reflow the unloadableDimensions ternary. - Add testStarTreeDimensionConvertedToRawWithSeparatedDictionary, covering the RAW forward index with separated dictionary configuration added in apache#19153. The dimension keeps its dictionary, so the star-tree stays loadable and must not be dropped by the new repair path.
|
Good point — I'd like to check we're targeting the same scenario, because I think there are two. What this PR fixes: the star-tree config is already gone from the table config (or The scenario your comment describes — star-tree still configured on the column while the column moves to If the former, I agree it's worth doing and it looks tractable: On the two inline questions about #19153: the check here is on the dictionary, not on the forward-index encoding, so a RAW-forward + separated-dictionary dimension is never flagged. |
Problem
A segment containing a star-tree whose dimension split order includes column
Xbecomespermanently unloadable if the table config is later changed so that
Xmoves tonoDictionaryColumnsand the segment is not re-pushed. It surfaces on the next segment(re)load — a restart, or any reload that re-opens the segment.
On load,
ForwardIndexHandlerconvertsXfrom dictionary to raw to match the currentnoDictionaryColumns, updating the column metadata. WithenableDynamicStarTreeCreation=false,SegmentPreProcessor#processStarTreesearly-returns, sothe now-stale star-tree is neither rebuilt nor removed.
StarTreeLoaderUtils#loadStarTreeV2thenbuilds a fixed-bit reader for the star-tree's
Xdimension and takes the bit width from themain column metadata (
ColumnMetadata#getBitsPerElement()), which isUNAVAILABLEfor a rawcolumn.
FixedBitIntReader#getReaderhits itsdefault:branch and throws, failing the wholesegment load.
Two properties make this hard to recover from:
already inconsistent on disk and
needProcess()reports nothing to do. A plainPOST /segments/{seg}/reloadre-runs the same failing path.because the star-tree still references
Xas a dictionary-encoded dimension.Fix
Three layers, in
pinot-segment-local:StarTreeBuilderUtils#findUnloadableDimensionsreportsstar-tree dimensions whose column no longer has a dictionary.
SegmentPreProcessor#needProcessStarTrees/#processStarTreesuse it to remove such star-treeseven when
enableDynamicStarTreeCreationis disabled. Removing star-trees only deletesfiles, so it is cheap enough for that flag; the rebuild the flag actually guards against stays
gated on it. When the flag is enabled the existing flow already rebuilds them, because the
split order no longer matches the builder configs.
StarTreeLoaderUtils#loadStarTreeV2skips an unreadablestar-tree with a
WARNnaming the segment, the star-tree and the offending column, instead ofthrowing. This covers segments whose pre-processing is skipped.
FixedBitIntReader#getReadernow reports the offending bit width rather thanthrowing a message-less
IllegalStateException.Note that
TableConfigUtils#validateStarTreeIndexConfigsalready rejects a star-tree config whosedimension has no dictionary, so the inconsistency only ever exists between the segment and the
config, never within the config itself. That is why this is fixed by repairing the segment rather
than by making
ForwardIndexHandlerrefuse the conversion — refusing it would silently ignorenoDictionaryColumnsand would still leave already-affected segments broken.Testing
Four tests in
SegmentPreProcessorTest, covering the drift with dynamic star-tree creationdisabled, the already-persisted variant where nothing else needs updating, the load path with
pre-processing skipped, and the dynamic-creation path (which was already safe —
MultipleTreesBuilder#getSeparatormoves the star-tree files aside before loading the segment).The first three fail on master with the stack above.
Existing suites pass:
SegmentPreProcessorTest,LoaderTest,ForwardIndexHandler*,*StarTree*andFixedBit*inpinot-segment-local, andorg.apache.pinot.core.startree.v2.*in
pinot-core.Release notes
Segments are no longer sent to ERROR when a star-tree dimension column is moved to
noDictionaryColumnswithout the segment being rebuilt. The stale star-tree is dropped on thenext reload (and rebuilt from the current config if
enableDynamicStarTreeCreationis enabled).