Skip to content

Commit aedae05

Browse files
xiangfu0Xiang Fu
authored andcommitted
Support codecSpec on segment reload and enable codecSpec end to end
1 parent 669848c commit aedae05

13 files changed

Lines changed: 632 additions & 132 deletions

File tree

pinot-segment-local/src/main/java/org/apache/pinot/segment/local/segment/creator/impl/openstruct/OpenStructColumnSplitter.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -309,9 +309,11 @@ private void writeDenseKeyColumn(String key)
309309

310310
boolean useDictionary = resolveUseDictionary(childFieldSpec, configsForDecision, statsCollector);
311311

312+
// Defense-in-depth mirror of OpenStructIndexType.validatePerKeyIndexes: the child forward config built
313+
// below discards any per-key codecSpec, so refuse to silently drop one that slipped past validation.
312314
ForwardIndexConfig configuredForwardIndex = configsForDecision.getConfig(StandardIndexes.forward());
313315
Preconditions.checkState(!configuredForwardIndex.hasCodecSpec(),
314-
"codecSpec is not supported yet for OPEN_STRUCT key: %s", key);
316+
"codecSpec is not supported for OPEN_STRUCT key: %s", key);
315317

316318
// Reconcile dictionary + forward encoding with the final decision (mirrors BaseSegmentCreator.adaptConfig);
317319
// ForwardIndexCreatorFactory selects dict-vs-raw from the forward config's EncodingType. A compression codec

pinot-segment-local/src/main/java/org/apache/pinot/segment/local/segment/index/forward/ForwardIndexCreatorFactory.java

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -92,18 +92,10 @@ public static ForwardIndexCreator createIndexCreator(IndexCreationContext contex
9292
chunkCompressionType = CodecSpecUtils.toLegacyChunkCompressionType(codecSpec);
9393
if (chunkCompressionType == null) {
9494
// The spec is either a transform or a compression-only spec with arguments that can't be
95-
// represented by a legacy ChunkCompressionType (e.g. ZSTD(5)). Both routes need V7. The
96-
// closed feature gate in ForwardIndexType.validate/shouldCreateIndex still rejects
97-
// codecSpec table configs, so this branch is only reachable through direct factory calls;
98-
// these checks are defense-in-depth for such paths.
99-
Preconditions.checkArgument(fieldSpec.isSingleValueField(),
100-
"codecSpec '%s' requires the V7 codec-pipeline writer (transform, chain, or non-default options), "
101-
+ "which only supports single-value columns. Column '%s' is multi-value.",
102-
codecSpec, columnName);
103-
Preconditions.checkArgument(storedType == DataType.INT || storedType == DataType.LONG,
104-
"codecSpec '%s' requires the V7 codec-pipeline writer (transform, chain, or non-default options), "
105-
+ "which only supports INT and LONG columns. Column '%s' has type: %s.",
106-
codecSpec, columnName, storedType);
95+
// represented by a legacy ChunkCompressionType (e.g. ZSTD(5)). Both routes need V7.
96+
// ForwardIndexType.validate enforces the same shape constraints at table-config time;
97+
// this check is defense-in-depth for direct factory calls that bypass validation.
98+
ForwardIndexType.validateV7WriterShape(codecSpec, fieldSpec);
10799
CodecPipelineExecutor executor = CodecPipelineExecutor.create(codecSpec, storedType);
108100
creator = new SingleValueFixedByteRawIndexCreator(indexDir, columnName, numTotalDocs, storedType,
109101
indexConfig.getTargetDocsPerChunk(), executor);

pinot-segment-local/src/main/java/org/apache/pinot/segment/local/segment/index/forward/ForwardIndexType.java

Lines changed: 53 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@
3131
import java.util.Map;
3232
import java.util.Set;
3333
import javax.annotation.Nullable;
34+
import org.apache.pinot.segment.local.io.codec.CodecPipelineExecutor;
35+
import org.apache.pinot.segment.local.io.codec.CodecSpecUtils;
3436
import org.apache.pinot.segment.local.realtime.impl.forward.CLPMutableForwardIndexV2;
3537
import org.apache.pinot.segment.local.realtime.impl.forward.FixedByteMVMutableForwardIndex;
3638
import org.apache.pinot.segment.local.realtime.impl.forward.FixedByteSVMutableForwardIndex;
@@ -104,24 +106,21 @@ public ForwardIndexConfig getDefaultConfig() {
104106
@Override
105107
public void validate(FieldIndexConfigs indexConfigs, FieldSpec fieldSpec, TableConfig tableConfig) {
106108
ForwardIndexConfig forwardIndexConfig = indexConfigs.getConfig(StandardIndexes.forward());
107-
rejectUnsupportedCodecSpec(forwardIndexConfig, fieldSpec.getName());
108109
if (forwardIndexConfig.isEnabled()) {
109110
validateForwardIndexEnabled(forwardIndexConfig, indexConfigs, fieldSpec);
110111
} else {
111112
validateForwardIndexDisabled(indexConfigs, fieldSpec, tableConfig);
112113
}
113114
}
114115

115-
static void rejectUnsupportedCodecSpec(ForwardIndexConfig config, String column) {
116-
Preconditions.checkState(!config.hasCodecSpec(),
117-
"codecSpec is not supported yet for column: %s", column);
118-
}
119-
120116
private void validateForwardIndexEnabled(ForwardIndexConfig forwardIndexConfig, FieldIndexConfigs indexConfigs,
121117
FieldSpec fieldSpec) {
122118
String column = fieldSpec.getName();
123119
CompressionCodec compressionCodec = forwardIndexConfig.getCompressionCodec();
124120
DictionaryIndexConfig dictionaryConfig = indexConfigs.getConfig(StandardIndexes.dictionary());
121+
if (forwardIndexConfig.hasCodecSpec()) {
122+
validateCodecSpec(forwardIndexConfig, fieldSpec);
123+
}
125124
// Dictionary-encoded forward index requires a dictionary to translate dict ids back to values.
126125
if (forwardIndexConfig.getEncodingType() == FieldConfig.EncodingType.DICTIONARY) {
127126
Preconditions.checkState(dictionaryConfig.isEnabled(),
@@ -143,6 +142,51 @@ private void validateForwardIndexEnabled(ForwardIndexConfig forwardIndexConfig,
143142
}
144143
}
145144

145+
/// Semantic validation for `codecSpec` at table-config time: parses and validates the pipeline
146+
/// (unknown codecs, stage ordering, per-codec type compatibility) against the column's stored
147+
/// type, then enforces the V7 codec-pipeline writer's shape constraints — single-value INT/LONG —
148+
/// for specs that cannot be served by the legacy raw forward-index formats.
149+
private void validateCodecSpec(ForwardIndexConfig forwardIndexConfig, FieldSpec fieldSpec) {
150+
String column = fieldSpec.getName();
151+
Preconditions.checkState(forwardIndexConfig.getEncodingType() == FieldConfig.EncodingType.RAW,
152+
"codecSpec requires RAW forward-index encoding for column: %s", column);
153+
FieldSpec.DataType storedType = fieldSpec.getDataType().getStoredType();
154+
155+
String codecSpec = forwardIndexConfig.getCodecSpec();
156+
try {
157+
// CodecPipelineExecutor.create parses the spec and runs full pipeline validation.
158+
CodecPipelineExecutor.create(codecSpec, storedType);
159+
} catch (IllegalArgumentException e) {
160+
throw new IllegalStateException(
161+
"Codec pipeline validation failed for column '" + column + "' (codecSpec='" + codecSpec + "'): "
162+
+ e.getMessage(), e);
163+
}
164+
// Transforms, chains, and non-default options all fail the legacy ChunkCompressionType mapping, so a
165+
// null mapping is exactly "needs the V7 writer".
166+
if (CodecSpecUtils.toLegacyChunkCompressionType(codecSpec) == null) {
167+
validateV7WriterShape(codecSpec, fieldSpec);
168+
}
169+
}
170+
171+
/// Shared V7 codec-pipeline writer shape constraints: specs that cannot be represented by a legacy
172+
/// [org.apache.pinot.segment.spi.compression.ChunkCompressionType] (transform, chain, or non-default
173+
/// options) require the V7 writer, which only supports single-value INT/LONG columns. Called at
174+
/// table-config validation time, and again from [ForwardIndexCreatorFactory] as defense-in-depth for
175+
/// direct factory calls that bypass validation.
176+
static void validateV7WriterShape(String codecSpec, FieldSpec fieldSpec) {
177+
String column = fieldSpec.getName();
178+
Preconditions.checkArgument(fieldSpec.isSingleValueField(),
179+
"codecSpec '%s' requires the V7 codec-pipeline writer (transform, chain, or non-default options), "
180+
+ "which only supports single-value columns. Column '%s' is multi-value; use a compression-only "
181+
+ "spec representable by ChunkCompressionType (LZ4, SNAPPY, GZIP, ZSTD/ZSTD(3)).",
182+
codecSpec, column);
183+
FieldSpec.DataType storedType = fieldSpec.getDataType().getStoredType();
184+
Preconditions.checkArgument(storedType == FieldSpec.DataType.INT || storedType == FieldSpec.DataType.LONG,
185+
"codecSpec '%s' requires the V7 codec-pipeline writer (transform, chain, or non-default options), "
186+
+ "which only supports INT and LONG columns. Column '%s' has type: %s.",
187+
codecSpec, column, storedType);
188+
}
189+
146190
private void validateForwardIndexDisabled(FieldIndexConfigs indexConfigs, FieldSpec fieldSpec,
147191
TableConfig tableConfig) {
148192
String column = fieldSpec.getName();
@@ -314,7 +358,6 @@ public static ChunkCompressionType getDefaultCompressionType(FieldSpec.FieldType
314358

315359
@Override
316360
public boolean shouldCreateIndex(IndexCreationContext context, ForwardIndexConfig indexConfig) {
317-
rejectUnsupportedCodecSpec(indexConfig, context.getFieldSpec().getName());
318361
return context.getFieldSpec().getDataType() != FieldSpec.DataType.OPEN_STRUCT;
319362
}
320363

@@ -377,7 +420,9 @@ public List<String> getFileExtensions(@Nullable ColumnMetadata columnMetadata) {
377420
@Nullable
378421
@Override
379422
public MutableIndex createMutableIndex(MutableIndexContext context, ForwardIndexConfig config) {
380-
rejectUnsupportedCodecSpec(config, context.getFieldSpec().getName());
423+
// Note: codecSpec applies only when an immutable segment is created (initial creation or
424+
// conversion/commit of a consuming segment). The mutable (consuming) forward index always uses
425+
// the standard in-memory formats below, so a configured codecSpec is intentionally ignored here.
381426
if (config.isDisabled()) {
382427
return null;
383428
}

0 commit comments

Comments
 (0)