This is an automated email from the ASF dual-hosted git repository.
lzljs3620320 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-paimon.git
The following commit(s) were added to refs/heads/master by this push:
new 5705fbd7c [flink] fix compaction multiple times (#2487)
5705fbd7c is described below
commit 5705fbd7cd93362a367aab3b0d2ed03ebedd7193
Author: YeJunHao <[email protected]>
AuthorDate: Tue Dec 12 15:56:08 2023 +0800
[flink] fix compaction multiple times (#2487)
---
.../paimon/flink/sink/StoreCompactOperator.java | 61 ++++++---
.../flink/sink/StoreCompactOperatorTest.java | 143 +++++++++++++++++++++
2 files changed, 187 insertions(+), 17 deletions(-)
diff --git
a/paimon-flink/paimon-flink-common/src/main/java/org/apache/paimon/flink/sink/StoreCompactOperator.java
b/paimon-flink/paimon-flink-common/src/main/java/org/apache/paimon/flink/sink/StoreCompactOperator.java
index 2af731eb3..86f72fe36 100644
---
a/paimon-flink/paimon-flink-common/src/main/java/org/apache/paimon/flink/sink/StoreCompactOperator.java
+++
b/paimon-flink/paimon-flink-common/src/main/java/org/apache/paimon/flink/sink/StoreCompactOperator.java
@@ -19,20 +19,25 @@
package org.apache.paimon.flink.sink;
import org.apache.paimon.CoreOptions;
+import org.apache.paimon.annotation.VisibleForTesting;
import org.apache.paimon.data.BinaryRow;
import org.apache.paimon.io.DataFileMeta;
import org.apache.paimon.io.DataFileMetaSerializer;
import org.apache.paimon.options.Options;
import org.apache.paimon.table.FileStoreTable;
+import org.apache.paimon.utils.Pair;
import org.apache.paimon.utils.Preconditions;
+import org.apache.flink.runtime.io.disk.iomanager.IOManager;
import org.apache.flink.runtime.state.StateInitializationContext;
import org.apache.flink.runtime.state.StateSnapshotContext;
import org.apache.flink.streaming.runtime.streamrecord.StreamRecord;
import org.apache.flink.table.data.RowData;
import java.io.IOException;
+import java.util.LinkedHashSet;
import java.util.List;
+import java.util.Set;
import static org.apache.paimon.utils.SerializationUtils.deserializeBinaryRow;
@@ -52,6 +57,7 @@ public class StoreCompactOperator extends
PrepareCommitOperator<RowData, Committ
private transient StoreSinkWriteState state;
private transient StoreSinkWrite write;
private transient DataFileMetaSerializer dataFileMetaSerializer;
+ private transient Set<Pair<BinaryRow, Integer>> waitToCompact;
public StoreCompactOperator(
FileStoreTable table,
@@ -77,30 +83,39 @@ public class StoreCompactOperator extends
PrepareCommitOperator<RowData, Committ
StateUtils.getSingleValueFromState(
context, "commit_user_state", String.class,
initialCommitUser);
- state =
- new StoreSinkWriteState(
- context,
- (tableName, partition, bucket) ->
- ChannelComputer.select(
- partition,
- bucket,
-
getRuntimeContext().getNumberOfParallelSubtasks())
- ==
getRuntimeContext().getIndexOfThisSubtask());
+ initStateAndWriter(
+ context,
+ (tableName, partition, bucket) ->
+ ChannelComputer.select(
+ partition,
+ bucket,
+
getRuntimeContext().getNumberOfParallelSubtasks())
+ == getRuntimeContext().getIndexOfThisSubtask(),
+ getContainingTask().getEnvironment().getIOManager(),
+ commitUser);
+ }
+
+ @VisibleForTesting
+ void initStateAndWriter(
+ StateInitializationContext context,
+ StoreSinkWriteState.StateValueFilter stateFilter,
+ IOManager ioManager,
+ String commitUser)
+ throws Exception {
+ // We put state and write init in this method for convenient testing.
Without construct a
+ // runtime context, we can test to construct a writer here
+ state = new StoreSinkWriteState(context, stateFilter);
write =
storeSinkWriteProvider.provide(
- table,
- commitUser,
- state,
- getContainingTask().getEnvironment().getIOManager(),
- memoryPool,
- getMetricGroup());
+ table, commitUser, state, ioManager, memoryPool,
getMetricGroup());
}
@Override
public void open() throws Exception {
super.open();
dataFileMetaSerializer = new DataFileMetaSerializer();
+ waitToCompact = new LinkedHashSet<>();
}
@Override
@@ -115,19 +130,31 @@ public class StoreCompactOperator extends
PrepareCommitOperator<RowData, Committ
if (write.streamingMode()) {
write.notifyNewFiles(snapshotId, partition, bucket, files);
- write.compact(partition, bucket, false);
} else {
Preconditions.checkArgument(
files.isEmpty(),
"Batch compact job does not concern what files are
compacted. "
+ "They only need to know what buckets are
compacted.");
- write.compact(partition, bucket, true);
}
+
+ waitToCompact.add(Pair.of(partition, bucket));
}
@Override
protected List<Committable> prepareCommit(boolean waitCompaction, long
checkpointId)
throws IOException {
+
+ try {
+ for (Pair<BinaryRow, Integer> partitionBucket : waitToCompact) {
+ write.compact(
+ partitionBucket.getKey(),
+ partitionBucket.getRight(),
+ !write.streamingMode());
+ }
+ } catch (Exception e) {
+ throw new RuntimeException("Exception happens while executing
compaction.", e);
+ }
+ waitToCompact.clear();
return write.prepareCommit(waitCompaction, checkpointId);
}
diff --git
a/paimon-flink/paimon-flink-common/src/test/java/org/apache/paimon/flink/sink/StoreCompactOperatorTest.java
b/paimon-flink/paimon-flink-common/src/test/java/org/apache/paimon/flink/sink/StoreCompactOperatorTest.java
new file mode 100644
index 000000000..c65190fd4
--- /dev/null
+++
b/paimon-flink/paimon-flink-common/src/test/java/org/apache/paimon/flink/sink/StoreCompactOperatorTest.java
@@ -0,0 +1,143 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.paimon.flink.sink;
+
+import org.apache.paimon.data.BinaryRow;
+import org.apache.paimon.data.GenericRow;
+import org.apache.paimon.data.InternalRow;
+import org.apache.paimon.flink.FlinkRowData;
+import org.apache.paimon.io.DataFileMeta;
+import org.apache.paimon.table.FileStoreTable;
+import org.apache.paimon.table.TableTestBase;
+import org.apache.paimon.table.sink.SinkRecord;
+
+import org.apache.flink.api.common.state.ListState;
+import org.apache.flink.api.common.state.ListStateDescriptor;
+import org.apache.flink.runtime.io.disk.iomanager.IOManagerAsync;
+import org.apache.flink.runtime.state.StateInitializationContextImpl;
+import
org.apache.flink.streaming.api.operators.collect.utils.MockOperatorStateStore;
+import org.apache.flink.streaming.runtime.streamrecord.StreamRecord;
+import org.apache.flink.table.data.RowData;
+import org.assertj.core.api.Assertions;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
+
+import java.util.List;
+
+/** Test for {@link StoreCompactOperator}. */
+public class StoreCompactOperatorTest extends TableTestBase {
+
+ @ParameterizedTest
+ @ValueSource(booleans = {true, false})
+ public void testCompactExactlyOnce(boolean streamingMode) throws Exception
{
+ createTableDefault();
+
+ CompactRememberStoreWrite compactRememberStoreWrite =
+ new CompactRememberStoreWrite(streamingMode);
+ StoreCompactOperator storeCompactOperator =
+ new StoreCompactOperator(
+ (FileStoreTable) getTableDefault(),
+ (table, commitUser, state, ioManager, memoryPool,
metricGroup) ->
+ compactRememberStoreWrite,
+ "10086");
+ storeCompactOperator.open();
+ StateInitializationContextImpl context =
+ new StateInitializationContextImpl(
+ null,
+ new MockOperatorStateStore() {
+ @Override
+ public <S> ListState<S> getUnionListState(
+ ListStateDescriptor<S> stateDescriptor)
throws Exception {
+ return getListState(stateDescriptor);
+ }
+ },
+ null,
+ null,
+ null);
+ storeCompactOperator.initStateAndWriter(
+ context, (a, b, c) -> true, new IOManagerAsync(), "123");
+
+ storeCompactOperator.processElement(new StreamRecord<>(data(0)));
+ storeCompactOperator.processElement(new StreamRecord<>(data(0)));
+ storeCompactOperator.processElement(new StreamRecord<>(data(1)));
+ storeCompactOperator.processElement(new StreamRecord<>(data(1)));
+ storeCompactOperator.processElement(new StreamRecord<>(data(2)));
+ storeCompactOperator.prepareCommit(true, 1);
+
+
Assertions.assertThat(compactRememberStoreWrite.compactTime).isEqualTo(3);
+ }
+
+ private RowData data(int bucket) {
+ GenericRow genericRow =
+ GenericRow.of(
+ 0L,
+ BinaryRow.EMPTY_ROW.toBytes(),
+ bucket,
+ new byte[] {0x00, 0x00, 0x00, 0x00});
+ return new FlinkRowData(genericRow);
+ }
+
+ private static class CompactRememberStoreWrite implements StoreSinkWrite {
+
+ private final boolean streamingMode;
+ private int compactTime = 0;
+
+ public CompactRememberStoreWrite(boolean streamingMode) {
+ this.streamingMode = streamingMode;
+ }
+
+ @Override
+ public SinkRecord write(InternalRow rowData) {
+ return null;
+ }
+
+ @Override
+ public SinkRecord toLogRecord(SinkRecord record) {
+ return null;
+ }
+
+ @Override
+ public void compact(BinaryRow partition, int bucket, boolean
fullCompaction) {
+ compactTime++;
+ }
+
+ @Override
+ public void notifyNewFiles(
+ long snapshotId, BinaryRow partition, int bucket,
List<DataFileMeta> files) {}
+
+ @Override
+ public List<Committable> prepareCommit(boolean waitCompaction, long
checkpointId) {
+ return null;
+ }
+
+ @Override
+ public void snapshotState() {}
+
+ @Override
+ public boolean streamingMode() {
+ return streamingMode;
+ }
+
+ @Override
+ public void close() {}
+
+ @Override
+ public void replace(FileStoreTable newTable) {}
+ }
+}