Skip to content

Commit 369fda4

Browse files
LsomeYeahTeng Huo
authored andcommitted
[flink] support performing incremental clustering by flink (apache#6395)
1 parent 3745c0a commit 369fda4

8 files changed

Lines changed: 896 additions & 12 deletions

File tree

docs/content/append-table/incremental-clustering.md

Lines changed: 45 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -95,11 +95,14 @@ clustering and small-file merging must be performed exclusively via Incremental
9595
## Run Incremental Clustering
9696
{{< hint info >}}
9797

98-
Currently, only support running Incremental Clustering in spark, support for flink will be added in the near future.
98+
only support running Incremental Clustering in batch mode.
9999

100100
{{< /hint >}}
101101

102-
To run a Incremental Clustering job, follow these instructions.
102+
To run a Incremental Clustering job, follow these instructions.
103+
104+
You don’t need to specify any clustering-related parameters when running Incremental Clustering,
105+
these options are already defined as table options. If you need to change clustering settings, please update the corresponding table options.
103106

104107
{{< tabs "incremental-clustering" >}}
105108

@@ -117,8 +120,46 @@ CALL sys.compact(table => 'T')
117120
-- run incremental clustering with full mode, this will recluster all data
118121
CALL sys.compact(table => 'T', compact_strategy => 'full')
119122
```
120-
You don’t need to specify any clustering-related parameters when running Incremental Clustering,
121-
these are already defined as table options. If you need to change clustering settings, please update the corresponding table options.
123+
{{< /tab >}}
124+
125+
{{< tab "Flink Action" >}}
126+
127+
Run the following command to submit a incremental clustering job for the table.
128+
129+
```bash
130+
<FLINK_HOME>/bin/flink run \
131+
/path/to/paimon-flink-action-{{< version >}}.jar \
132+
compact \
133+
--warehouse <warehouse-path> \
134+
--database <database-name> \
135+
--table <table-name> \
136+
[--compact_strategy <minor / full>] \
137+
[--table_conf <table_conf>] \
138+
[--catalog_conf <paimon-catalog-conf> [--catalog_conf <paimon-catalog-conf> ...]]
139+
```
140+
141+
Example: run incremental clustering
142+
143+
```bash
144+
<FLINK_HOME>/bin/flink run \
145+
/path/to/paimon-flink-action-{{< version >}}.jar \
146+
compact \
147+
--warehouse s3:///path/to/warehouse \
148+
--database test_db \
149+
--table test_table \
150+
--table_conf sink.parallelism=2 \
151+
--compact_strategy minor \
152+
--catalog_conf s3.endpoint=https://****.com \
153+
--catalog_conf s3.access-key=***** \
154+
--catalog_conf s3.secret-key=*****
155+
```
156+
* `--compact_strategy` Determines how to pick files to be cluster, the default is `minor`.
157+
* `full` : All files will be selected for clustered.
158+
* `minor` : Pick the set of files that need to be clustered based on specified conditions.
159+
160+
Note: write parallelism is set by `sink.parallelism`, if too big, may generate a large number of small files.
161+
162+
You can use `-D execution.runtime-mode=batch` or `-yD execution.runtime-mode=batch` (for the ON-YARN scenario) to use batch mode.
122163
{{< /tab >}}
123164
124165
{{< /tabs >}}

paimon-core/src/main/java/org/apache/paimon/append/cluster/IncrementalClusterManager.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,8 @@ public List<DataSplit> toSplits(BinaryRow partition, List<DataFileMeta> files) {
226226
return splits;
227227
}
228228

229-
public List<DataFileMeta> upgrade(List<DataFileMeta> filesAfterCluster, int outputLevel) {
229+
public static List<DataFileMeta> upgrade(
230+
List<DataFileMeta> filesAfterCluster, int outputLevel) {
230231
return filesAfterCluster.stream()
231232
.map(file -> file.upgrade(outputLevel))
232233
.collect(Collectors.toList());

paimon-flink/paimon-flink-common/src/main/java/org/apache/paimon/flink/action/CompactAction.java

Lines changed: 147 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,13 @@
1919
package org.apache.paimon.flink.action;
2020

2121
import org.apache.paimon.CoreOptions;
22+
import org.apache.paimon.append.cluster.IncrementalClusterManager;
23+
import org.apache.paimon.compact.CompactUnit;
2224
import org.apache.paimon.data.BinaryRow;
2325
import org.apache.paimon.data.InternalRow;
2426
import org.apache.paimon.flink.FlinkConnectorOptions;
27+
import org.apache.paimon.flink.cluster.IncrementalClusterSplitSource;
28+
import org.apache.paimon.flink.cluster.RewriteIncrementalClusterCommittableOperator;
2529
import org.apache.paimon.flink.compact.AppendTableCompactBuilder;
2630
import org.apache.paimon.flink.postpone.PostponeBucketCompactSplitSource;
2731
import org.apache.paimon.flink.postpone.RewritePostponeBucketCommittableOperator;
@@ -32,7 +36,10 @@
3236
import org.apache.paimon.flink.sink.FixedBucketSink;
3337
import org.apache.paimon.flink.sink.FlinkSinkBuilder;
3438
import org.apache.paimon.flink.sink.FlinkStreamPartitioner;
39+
import org.apache.paimon.flink.sink.RowAppendTableSink;
3540
import org.apache.paimon.flink.sink.RowDataChannelComputer;
41+
import org.apache.paimon.flink.sorter.TableSortInfo;
42+
import org.apache.paimon.flink.sorter.TableSorter;
3643
import org.apache.paimon.flink.source.CompactorSourceBuilder;
3744
import org.apache.paimon.lock.CompactLockOptions;
3845
import org.apache.paimon.manifest.ManifestEntry;
@@ -44,6 +51,7 @@
4451
import org.apache.paimon.predicate.PredicateProjectionConverter;
4552
import org.apache.paimon.table.BucketMode;
4653
import org.apache.paimon.table.FileStoreTable;
54+
import org.apache.paimon.table.source.DataSplit;
4755
import org.apache.paimon.types.RowType;
4856
import org.apache.paimon.utils.InternalRowPartitionComputer;
4957
import org.apache.paimon.utils.Pair;
@@ -68,6 +76,7 @@
6876
import java.util.LinkedHashMap;
6977
import java.util.List;
7078
import java.util.Map;
79+
import java.util.stream.Collectors;
7180

7281
import static org.apache.paimon.partition.PartitionPredicate.createBinaryPartitions;
7382
import static org.apache.paimon.partition.PartitionPredicate.createPartitionPredicate;
@@ -101,10 +110,6 @@ public CompactAction(
101110
checkArgument(
102111
!((FileStoreTable) table).coreOptions().dataEvolutionEnabled(),
103112
"Compact action does not support data evolution table yet. ");
104-
checkArgument(
105-
!(((FileStoreTable) table).bucketMode() == BucketMode.BUCKET_UNAWARE
106-
&& ((FileStoreTable) table).coreOptions().clusteringIncrementalEnabled()),
107-
"The table has enabled incremental clustering, and do not support compact in flink yet.");
108113
HashMap<String, String> dynamicOptions = new HashMap<>(tableConf);
109114
dynamicOptions.put(CoreOptions.WRITE_ONLY.key(), "false");
110115
// Disable compaction lock in StoreCommitter in CompactAction.
@@ -156,8 +161,12 @@ private boolean buildImpl() throws Exception {
156161
if (fileStoreTable.coreOptions().bucket() == BucketMode.POSTPONE_BUCKET) {
157162
return buildForPostponeBucketCompaction(env, fileStoreTable, isStreaming);
158163
} else if (fileStoreTable.bucketMode() == BucketMode.BUCKET_UNAWARE) {
159-
buildForAppendTableCompact(env, fileStoreTable, isStreaming);
160-
return true;
164+
if (fileStoreTable.coreOptions().clusteringIncrementalEnabled()) {
165+
return buildForIncrementalClustering(env, fileStoreTable, isStreaming);
166+
} else {
167+
buildForAppendTableCompact(env, fileStoreTable, isStreaming);
168+
return true;
169+
}
161170
} else {
162171
buildForBucketedTableCompact(env, fileStoreTable, isStreaming);
163172
return true;
@@ -211,6 +220,138 @@ private void buildForAppendTableCompact(
211220
builder.build();
212221
}
213222

223+
private boolean buildForIncrementalClustering(
224+
StreamExecutionEnvironment env, FileStoreTable table, boolean isStreaming) {
225+
checkArgument(!isStreaming, "Incremental clustering currently only supports batch mode");
226+
checkArgument(
227+
partitions == null,
228+
"Incremental clustering currently does not support specifying partitions");
229+
checkArgument(
230+
whereSql == null, "Incremental clustering currently does not support predicates");
231+
232+
IncrementalClusterManager incrementalClusterManager = new IncrementalClusterManager(table);
233+
234+
// non-full strategy as default for incremental clustering
235+
if (fullCompaction == null) {
236+
fullCompaction = false;
237+
}
238+
Options options = new Options(table.options());
239+
int localSampleMagnification = table.coreOptions().getLocalSampleMagnification();
240+
if (localSampleMagnification < 20) {
241+
throw new IllegalArgumentException(
242+
String.format(
243+
"the config '%s=%d' should not be set too small, greater than or equal to 20 is needed.",
244+
CoreOptions.SORT_COMPACTION_SAMPLE_MAGNIFICATION.key(),
245+
localSampleMagnification));
246+
}
247+
String commitUser = CoreOptions.createCommitUser(options);
248+
InternalRowPartitionComputer partitionComputer =
249+
new InternalRowPartitionComputer(
250+
table.coreOptions().partitionDefaultName(),
251+
table.store().partitionType(),
252+
table.partitionKeys().toArray(new String[0]),
253+
table.coreOptions().legacyPartitionName());
254+
255+
// 1. pick cluster files for each partition
256+
Map<BinaryRow, CompactUnit> compactUnits =
257+
incrementalClusterManager.prepareForCluster(fullCompaction);
258+
if (compactUnits.isEmpty()) {
259+
LOGGER.info(
260+
"No partition needs to be incrementally clustered. "
261+
+ "Please set '--compact_strategy full' if you need to forcibly trigger the cluster.");
262+
if (this.forceStartFlinkJob) {
263+
env.fromSequence(0, 0)
264+
.name("Nothing to Cluster Source")
265+
.sinkTo(new DiscardingSink<>());
266+
return true;
267+
} else {
268+
return false;
269+
}
270+
}
271+
Map<BinaryRow, DataSplit[]> partitionSplits =
272+
compactUnits.entrySet().stream()
273+
.collect(
274+
Collectors.toMap(
275+
Map.Entry::getKey,
276+
entry ->
277+
incrementalClusterManager
278+
.toSplits(
279+
entry.getKey(),
280+
entry.getValue().files())
281+
.toArray(new DataSplit[0])));
282+
283+
// 2. read,sort and write in partition
284+
List<DataStream<Committable>> dataStreams = new ArrayList<>();
285+
286+
for (Map.Entry<BinaryRow, DataSplit[]> entry : partitionSplits.entrySet()) {
287+
DataSplit[] splits = entry.getValue();
288+
LinkedHashMap<String, String> partitionSpec =
289+
partitionComputer.generatePartValues(entry.getKey());
290+
// 2.1 generate source for current partition
291+
Pair<DataStream<RowData>, DataStream<Committable>> sourcePair =
292+
IncrementalClusterSplitSource.buildSource(
293+
env,
294+
table,
295+
partitionSpec,
296+
splits,
297+
options.get(FlinkConnectorOptions.SCAN_PARALLELISM));
298+
299+
// 2.2 cluster in partition
300+
Integer sinkParallelism = options.get(FlinkConnectorOptions.SINK_PARALLELISM);
301+
if (sinkParallelism == null) {
302+
sinkParallelism = sourcePair.getLeft().getParallelism();
303+
}
304+
TableSortInfo sortInfo =
305+
new TableSortInfo.Builder()
306+
.setSortColumns(incrementalClusterManager.clusterKeys())
307+
.setSortStrategy(incrementalClusterManager.clusterCurve())
308+
.setSinkParallelism(sinkParallelism)
309+
.setLocalSampleSize(sinkParallelism * localSampleMagnification)
310+
.setGlobalSampleSize(sinkParallelism * 1000)
311+
.setRangeNumber(sinkParallelism * 10)
312+
.build();
313+
DataStream<RowData> sorted =
314+
TableSorter.getSorter(env, sourcePair.getLeft(), table, sortInfo).sort();
315+
316+
// 2.3 write and then reorganize the committable
317+
// set parallelism to null, and it'll forward parallelism when doWrite()
318+
RowAppendTableSink sink = new RowAppendTableSink(table, null, null, null);
319+
boolean blobAsDescriptor = table.coreOptions().blobAsDescriptor();
320+
DataStream<Committable> clusterCommittable =
321+
sink.doWrite(
322+
FlinkSinkBuilder.mapToInternalRow(
323+
sorted,
324+
table.rowType(),
325+
blobAsDescriptor,
326+
table.catalogEnvironment().catalogContext()),
327+
commitUser,
328+
null)
329+
.transform(
330+
"Rewrite cluster committable",
331+
new CommittableTypeInfo(),
332+
new RewriteIncrementalClusterCommittableOperator(
333+
table,
334+
compactUnits.entrySet().stream()
335+
.collect(
336+
Collectors.toMap(
337+
Map.Entry::getKey,
338+
unit ->
339+
unit.getValue()
340+
.outputLevel()))));
341+
dataStreams.add(clusterCommittable);
342+
dataStreams.add(sourcePair.getRight());
343+
}
344+
345+
// 3. commit
346+
RowAppendTableSink sink = new RowAppendTableSink(table, null, null, null);
347+
DataStream<Committable> dataStream = dataStreams.get(0);
348+
for (int i = 1; i < dataStreams.size(); i++) {
349+
dataStream = dataStream.union(dataStreams.get(i));
350+
}
351+
sink.doCommit(dataStream, commitUser);
352+
return true;
353+
}
354+
214355
protected PartitionPredicate getPartitionPredicate() throws Exception {
215356
checkArgument(
216357
partitions == null || whereSql == null,

0 commit comments

Comments
 (0)