You are viewing a plain text version of this content. The canonical link for it is here.
Posted to issues@paimon.apache.org by "JingsongLi (via GitHub)" <gi...@apache.org> on 2025/06/23 07:22:40 UTC
[PR] [core][flink] Introduce write operator coordinator to optimize write restore [paimon]
JingsongLi opened a new pull request, #5787:
URL: https://github.com/apache/paimon/pull/5787
<!-- Please specify the module before the PR name: [core] ... or [flink] ... -->
### Purpose
<!-- Linking this pull request to the issue -->
At present, each write operator initializes every time a new bucket appears, and the initialization process restores the historical data files. Each bucket performs this process once, which is very wasteful and results in a large amount of duplicate IO.
This PR uses Flink's operator coordinator capability to make this restore a global single point execution, so that we can add cache in the coordinator to optimize the issue of duplicate IO.
<!-- What is the purpose of the change -->
### Tests
<!-- List UT and IT cases to verify this change -->
### API and Format
<!-- Does this change affect API or storage format -->
### Documentation
<!-- Does this change introduce a new feature -->
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: issues-unsubscribe@paimon.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] [core][flink] Introduce write operator coordinator to optimize write restore [paimon]
Posted by "JingsongLi (via GitHub)" <gi...@apache.org>.
JingsongLi commented on code in PR #5787:
URL: https://github.com/apache/paimon/pull/5787#discussion_r2161362839
##########
paimon-flink/paimon-flink-common/src/main/java/org/apache/paimon/flink/sink/WriteOperatorCoordinator.java:
##########
@@ -0,0 +1,175 @@
+/*
+ * 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.CoreOptions;
+import org.apache.paimon.Snapshot;
+import org.apache.paimon.data.BinaryRow;
+import org.apache.paimon.fs.Path;
+import org.apache.paimon.io.DataFileMeta;
+import org.apache.paimon.manifest.ManifestEntry;
+import org.apache.paimon.operation.FileStoreScan;
+import org.apache.paimon.operation.write.WriteRestore;
+import org.apache.paimon.options.MemorySize;
+import org.apache.paimon.table.FileStoreTable;
+import org.apache.paimon.utils.SegmentsCache;
+
+import org.apache.flink.runtime.jobgraph.OperatorID;
+import org.apache.flink.runtime.operators.coordination.CoordinationRequest;
+import org.apache.flink.runtime.operators.coordination.CoordinationRequestHandler;
+import org.apache.flink.runtime.operators.coordination.CoordinationResponse;
+import org.apache.flink.runtime.operators.coordination.OperatorCoordinator;
+import org.apache.flink.runtime.operators.coordination.OperatorEvent;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ThreadPoolExecutor;
+
+import static org.apache.paimon.flink.FlinkConnectorOptions.SINK_WRITER_COORDINATOR_CACHE_MEMORY;
+import static org.apache.paimon.utils.SerializationUtils.deserializeBinaryRow;
+import static org.apache.paimon.utils.ThreadPoolUtils.createCachedThreadPool;
+
+/**
+ * {@link OperatorCoordinator} for {@link TableWriteOperator}, to use a single point to obtain the
+ * list of initialization files required for write operators.
+ */
+public class WriteOperatorCoordinator implements OperatorCoordinator, CoordinationRequestHandler {
+
+ private final FileStoreTable table;
+
+ private ThreadPoolExecutor executor;
+
+ private volatile Snapshot snapshot;
+ private volatile FileStoreScan scan;
+
+ public WriteOperatorCoordinator(FileStoreTable table) {
+ this.table = table;
+ }
+
+ private synchronized void refreshOrCreateScan() {
+ Optional<Snapshot> latestSnapshot = table.latestSnapshot();
+ if (!latestSnapshot.isPresent()) {
+ return;
+ }
+ if (scan == null) {
+ scan = table.store().newScan();
+ if (table.coreOptions().manifestDeleteFileDropStats()) {
+ scan.dropStats();
+ }
+ }
+ snapshot = latestSnapshot.get();
+ scan.withSnapshot(snapshot);
+ }
+
+ private synchronized WriteCoordinationResponse scanDataFiles(WriteCoordinationRequest request)
+ throws IOException {
+ if (scan == null) {
+ return new WriteCoordinationResponse(null, null, null);
+ }
+ BinaryRow partition = deserializeBinaryRow(request.partition());
+ int bucket = request.bucket();
+ List<DataFileMeta> restoreFiles = new ArrayList<>();
+ List<ManifestEntry> entries = scan.withPartitionBucket(partition, bucket).plan().files();
+ Integer totalBuckets = WriteRestore.extractDataFiles(entries, restoreFiles);
+ return new WriteCoordinationResponse(snapshot, restoreFiles, totalBuckets);
+ }
+
+ @Override
+ public void start() throws Exception {
+ executor = createCachedThreadPool(1, "WriteCoordinator");
Review Comment:
To avoid thread safety issues, only single thread
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: issues-unsubscribe@paimon.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] [core][flink] Introduce write operator coordinator to optimize write restore [paimon]
Posted by "JingsongLi (via GitHub)" <gi...@apache.org>.
JingsongLi commented on code in PR #5787:
URL: https://github.com/apache/paimon/pull/5787#discussion_r2163958967
##########
paimon-core/src/main/java/org/apache/paimon/operation/AbstractFileStoreWrite.java:
##########
@@ -94,20 +98,19 @@ public abstract class AbstractFileStoreWrite<T> implements FileStoreWrite<T> {
protected AbstractFileStoreWrite(
SnapshotManager snapshotManager,
FileStoreScan scan,
- @Nullable IndexMaintainer.Factory<T> indexFactory,
+ @Nullable DynamicBucketIndexMaintainer.Factory dbMaintainerFactory,
@Nullable DeletionVectorsMaintainer.Factory dvMaintainerFactory,
String tableName,
CoreOptions options,
RowType partitionType) {
- this.snapshotManager = snapshotManager;
- this.scan = scan;
- // Statistic is useless in writer
- if (options.manifestDeleteFileDropStats()) {
- if (this.scan != null) {
- this.scan.dropStats();
- }
+ IndexFileHandler indexFileHandler = null;
+ if (dbMaintainerFactory != null) {
+ indexFileHandler = dbMaintainerFactory.indexFileHandler();
+ } else if (dvMaintainerFactory != null) {
+ indexFileHandler = dvMaintainerFactory.indexFileHandler();
}
- this.indexFactory = indexFactory;
+ this.restore = new FileSystemWriteRestore(options, snapshotManager, scan, indexFileHandler);
Review Comment:
The cost of refactoring is too high, and calling `FileSystemWriteRestore` at the outermost layer requires the introduction of a significant amount of code intrusion. I prefer to not do this refactor.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: issues-unsubscribe@paimon.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] [core][flink] Introduce write operator coordinator to optimize write restore [paimon]
Posted by "wwj6591812 (via GitHub)" <gi...@apache.org>.
wwj6591812 commented on code in PR #5787:
URL: https://github.com/apache/paimon/pull/5787#discussion_r2160986066
##########
paimon-flink/paimon-flink-common/src/main/java/org/apache/paimon/flink/FlinkConnectorOptions.java:
##########
@@ -523,6 +523,20 @@ public class FlinkConnectorOptions {
"Commit listener will be called after a successful commit. This option list custom commit "
+ "listener identifiers separated by comma.");
+ public static final ConfigOption<Boolean> SINK_WRITER_COORDINATOR_ENABLED =
+ key("sink.writer-coordinator.enabled")
+ .booleanType()
+ .defaultValue(false)
Review Comment:
Why default value is false?
##########
paimon-flink/paimon-flink-common/src/main/java/org/apache/paimon/flink/sink/WriteOperatorCoordinator.java:
##########
@@ -0,0 +1,175 @@
+/*
+ * 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.CoreOptions;
+import org.apache.paimon.Snapshot;
+import org.apache.paimon.data.BinaryRow;
+import org.apache.paimon.fs.Path;
+import org.apache.paimon.io.DataFileMeta;
+import org.apache.paimon.manifest.ManifestEntry;
+import org.apache.paimon.operation.FileStoreScan;
+import org.apache.paimon.operation.write.WriteRestore;
+import org.apache.paimon.options.MemorySize;
+import org.apache.paimon.table.FileStoreTable;
+import org.apache.paimon.utils.SegmentsCache;
+
+import org.apache.flink.runtime.jobgraph.OperatorID;
+import org.apache.flink.runtime.operators.coordination.CoordinationRequest;
+import org.apache.flink.runtime.operators.coordination.CoordinationRequestHandler;
+import org.apache.flink.runtime.operators.coordination.CoordinationResponse;
+import org.apache.flink.runtime.operators.coordination.OperatorCoordinator;
+import org.apache.flink.runtime.operators.coordination.OperatorEvent;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ThreadPoolExecutor;
+
+import static org.apache.paimon.flink.FlinkConnectorOptions.SINK_WRITER_COORDINATOR_CACHE_MEMORY;
+import static org.apache.paimon.utils.SerializationUtils.deserializeBinaryRow;
+import static org.apache.paimon.utils.ThreadPoolUtils.createCachedThreadPool;
+
+/**
+ * {@link OperatorCoordinator} for {@link TableWriteOperator}, to use a single point to obtain the
+ * list of initialization files required for write operators.
+ */
+public class WriteOperatorCoordinator implements OperatorCoordinator, CoordinationRequestHandler {
+
+ private final FileStoreTable table;
+
+ private ThreadPoolExecutor executor;
+
+ private volatile Snapshot snapshot;
+ private volatile FileStoreScan scan;
+
+ public WriteOperatorCoordinator(FileStoreTable table) {
+ this.table = table;
+ }
+
+ private synchronized void refreshOrCreateScan() {
+ Optional<Snapshot> latestSnapshot = table.latestSnapshot();
+ if (!latestSnapshot.isPresent()) {
+ return;
+ }
+ if (scan == null) {
+ scan = table.store().newScan();
+ if (table.coreOptions().manifestDeleteFileDropStats()) {
+ scan.dropStats();
+ }
+ }
+ snapshot = latestSnapshot.get();
+ scan.withSnapshot(snapshot);
+ }
+
+ private synchronized WriteCoordinationResponse scanDataFiles(WriteCoordinationRequest request)
+ throws IOException {
+ if (scan == null) {
+ return new WriteCoordinationResponse(null, null, null);
+ }
+ BinaryRow partition = deserializeBinaryRow(request.partition());
+ int bucket = request.bucket();
+ List<DataFileMeta> restoreFiles = new ArrayList<>();
+ List<ManifestEntry> entries = scan.withPartitionBucket(partition, bucket).plan().files();
+ Integer totalBuckets = WriteRestore.extractDataFiles(entries, restoreFiles);
+ return new WriteCoordinationResponse(snapshot, restoreFiles, totalBuckets);
+ }
+
+ @Override
+ public void start() throws Exception {
+ executor = createCachedThreadPool(1, "WriteCoordinator");
Review Comment:
only 1 thread?
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: issues-unsubscribe@paimon.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] [core][flink] Introduce write operator coordinator to optimize write restore [paimon]
Posted by "JingsongLi (via GitHub)" <gi...@apache.org>.
JingsongLi commented on code in PR #5787:
URL: https://github.com/apache/paimon/pull/5787#discussion_r2163201641
##########
paimon-flink/paimon-flink-common/src/main/java/org/apache/paimon/flink/sink/coordinator/WriteOperatorCoordinator.java:
##########
@@ -0,0 +1,228 @@
+/*
+ * 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.coordinator;
+
+import org.apache.paimon.CoreOptions;
+import org.apache.paimon.Snapshot;
+import org.apache.paimon.data.BinaryRow;
+import org.apache.paimon.flink.sink.TableWriteOperator;
+import org.apache.paimon.fs.Path;
+import org.apache.paimon.index.IndexFileHandler;
+import org.apache.paimon.index.IndexFileMeta;
+import org.apache.paimon.io.DataFileMeta;
+import org.apache.paimon.manifest.ManifestEntry;
+import org.apache.paimon.operation.FileStoreScan;
+import org.apache.paimon.operation.write.WriteRestore;
+import org.apache.paimon.options.MemorySize;
+import org.apache.paimon.table.FileStoreTable;
+import org.apache.paimon.utils.SegmentsCache;
+
+import org.apache.flink.runtime.jobgraph.OperatorID;
+import org.apache.flink.runtime.operators.coordination.CoordinationRequest;
+import org.apache.flink.runtime.operators.coordination.CoordinationRequestHandler;
+import org.apache.flink.runtime.operators.coordination.CoordinationResponse;
+import org.apache.flink.runtime.operators.coordination.OperatorCoordinator;
+import org.apache.flink.runtime.operators.coordination.OperatorEvent;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ThreadPoolExecutor;
+
+import static org.apache.paimon.deletionvectors.DeletionVectorsIndexFile.DELETION_VECTORS_INDEX;
+import static org.apache.paimon.flink.FlinkConnectorOptions.SINK_WRITER_COORDINATOR_CACHE_MEMORY;
+import static org.apache.paimon.utils.SerializationUtils.deserializeBinaryRow;
+import static org.apache.paimon.utils.ThreadPoolUtils.createCachedThreadPool;
+
+/**
+ * {@link OperatorCoordinator} for {@link TableWriteOperator}, to use a single point to obtain the
+ * list of initialization files required for write operators.
+ */
+public class WriteOperatorCoordinator implements OperatorCoordinator, CoordinationRequestHandler {
+
+ private final FileStoreTable table;
+
+ private ThreadPoolExecutor executor;
+ private Map<String, Long> latestCommittedIdentifiers;
+
+ private volatile Snapshot snapshot;
+ private volatile FileStoreScan dataFileScan;
+ private volatile IndexFileHandler indexFileHandler;
+
+ public WriteOperatorCoordinator(FileStoreTable table) {
+ this.table = table;
+ }
+
+ private synchronized void refreshOrCreateScan() {
+ Optional<Snapshot> latestSnapshot = table.latestSnapshot();
+ if (!latestSnapshot.isPresent()) {
+ return;
+ }
+ if (dataFileScan == null) {
+ dataFileScan = table.store().newScan();
+ if (table.coreOptions().manifestDeleteFileDropStats()) {
+ dataFileScan.dropStats();
+ }
+ }
+ if (indexFileHandler == null) {
+ indexFileHandler = table.store().newIndexFileHandler();
+ }
+ snapshot = latestSnapshot.get();
+ dataFileScan.withSnapshot(snapshot);
+ }
+
+ private synchronized ScanCoordinationResponse scanDataFiles(ScanCoordinationRequest request)
+ throws IOException {
+ if (snapshot == null) {
+ return new ScanCoordinationResponse(null, null, null, null, null);
+ }
+
+ BinaryRow partition = deserializeBinaryRow(request.partition());
+ int bucket = request.bucket();
+
+ List<DataFileMeta> restoreFiles = new ArrayList<>();
+ List<ManifestEntry> entries =
+ dataFileScan.withPartitionBucket(partition, bucket).plan().files();
+ Integer totalBuckets = WriteRestore.extractDataFiles(entries, restoreFiles);
+
+ IndexFileMeta dynamicBucketIndex = null;
+ if (request.scanDynamicBucketIndex()) {
+ dynamicBucketIndex =
+ indexFileHandler.scanHashIndex(snapshot, partition, bucket).orElse(null);
+ }
+
+ List<IndexFileMeta> deleteVectorsIndex = null;
+ if (request.scanDeleteVectorsIndex()) {
+ deleteVectorsIndex =
+ indexFileHandler.scan(snapshot, DELETION_VECTORS_INDEX, partition, bucket);
+ }
+
+ return new ScanCoordinationResponse(
+ snapshot, totalBuckets, restoreFiles, dynamicBucketIndex, deleteVectorsIndex);
+ }
+
+ private synchronized LatestIdentifierResponse latestCommittedIdentifier(
+ LatestIdentifierRequest request) {
+ String user = request.user();
+ long identifier =
+ latestCommittedIdentifiers.computeIfAbsent(
+ user,
+ k ->
+ table.snapshotManager()
+ .latestSnapshotOfUser(user)
+ .map(Snapshot::commitIdentifier)
+ .orElse(Long.MIN_VALUE));
+ return new LatestIdentifierResponse(identifier);
+ }
Review Comment:
done
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: issues-unsubscribe@paimon.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] [core][flink] Introduce write operator coordinator to optimize write restore [paimon]
Posted by "tsreaper (via GitHub)" <gi...@apache.org>.
tsreaper commented on code in PR #5787:
URL: https://github.com/apache/paimon/pull/5787#discussion_r2165481264
##########
paimon-flink/paimon-flink-common/src/test/java/org/apache/paimon/flink/sink/coordinator/TableWriteCoordinatorTest.java:
##########
@@ -0,0 +1,73 @@
+/*
+ * 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.coordinator;
+
+import org.apache.paimon.Snapshot;
+import org.apache.paimon.catalog.Identifier;
+import org.apache.paimon.data.GenericRow;
+import org.apache.paimon.schema.Schema;
+import org.apache.paimon.table.FileStoreTable;
+import org.apache.paimon.table.TableTestBase;
+import org.apache.paimon.types.DataTypes;
+
+import org.junit.jupiter.api.Test;
+
+import static org.apache.paimon.data.BinaryRow.EMPTY_ROW;
+import static org.apache.paimon.utils.SerializationUtils.serializeBinaryRow;
+import static org.assertj.core.api.Assertions.assertThat;
+
+class TableWriteCoordinatorTest extends TableTestBase {
+
+ @Test
+ public void testLatestIdentifierAndScan1() throws Exception {
+ innerTestLatestIdentifierAndScan(true);
+ }
+
+ @Test
+ public void testLatestIdentifierAndScan2() throws Exception {
+ innerTestLatestIdentifierAndScan(false);
+ }
+
+ private void innerTestLatestIdentifierAndScan(boolean initSnapshot) throws Exception {
Review Comment:
```suggestion
@ParameterizedTest
@ValueSource(booleans = {true, false})
public void testLatestIdentifierAndScan(boolean initSnapshot) throws Exception {
```
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: issues-unsubscribe@paimon.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] [core][flink] Introduce write operator coordinator to optimize write restore [paimon]
Posted by "tsreaper (via GitHub)" <gi...@apache.org>.
tsreaper commented on code in PR #5787:
URL: https://github.com/apache/paimon/pull/5787#discussion_r2165431772
##########
paimon-flink/paimon-flink-common/src/main/java/org/apache/paimon/flink/sink/coordinator/TableWriteCoordinator.java:
##########
@@ -0,0 +1,126 @@
+/*
+ * 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.coordinator;
+
+import org.apache.paimon.Snapshot;
+import org.apache.paimon.data.BinaryRow;
+import org.apache.paimon.index.IndexFileHandler;
+import org.apache.paimon.index.IndexFileMeta;
+import org.apache.paimon.io.DataFileMeta;
+import org.apache.paimon.manifest.ManifestEntry;
+import org.apache.paimon.operation.FileStoreScan;
+import org.apache.paimon.operation.WriteRestore;
+import org.apache.paimon.table.FileStoreTable;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.concurrent.ConcurrentHashMap;
+
+import static org.apache.paimon.deletionvectors.DeletionVectorsIndexFile.DELETION_VECTORS_INDEX;
+import static org.apache.paimon.utils.SerializationUtils.deserializeBinaryRow;
+
+/**
+ * Coordinator for a table, to use a single point to obtain the list of initialization files
+ * required for write operators.
+ */
+public class TableWriteCoordinator {
+
+ private final FileStoreTable table;
+ private final Map<String, Long> latestCommittedIdentifiers;
+ private final FileStoreScan scan;
+ private final IndexFileHandler indexFileHandler;
+
+ private volatile Snapshot snapshot;
+
+ public TableWriteCoordinator(FileStoreTable table) {
+ this.table = table;
+ this.latestCommittedIdentifiers = new ConcurrentHashMap<>();
+ this.scan = table.store().newScan();
+ if (table.coreOptions().manifestDeleteFileDropStats()) {
+ scan.dropStats();
+ }
+ this.indexFileHandler = table.store().newIndexFileHandler();
+ refresh();
+ }
+
+ private synchronized void refresh() {
+ Optional<Snapshot> latestSnapshot = table.latestSnapshot();
+ if (!latestSnapshot.isPresent()) {
+ return;
+ }
+ this.snapshot = latestSnapshot.get();
+ this.scan.withSnapshot(snapshot);
+ }
+
+ public synchronized ScanCoordinationResponse scan(ScanCoordinationRequest request)
+ throws IOException {
+ if (snapshot == null) {
+ return new ScanCoordinationResponse(null, null, null, null, null);
+ }
+
+ BinaryRow partition = deserializeBinaryRow(request.partition());
+ int bucket = request.bucket();
+
+ List<DataFileMeta> restoreFiles = new ArrayList<>();
+ List<ManifestEntry> entries = scan.withPartitionBucket(partition, bucket).plan().files();
+ Integer totalBuckets = WriteRestore.extractDataFiles(entries, restoreFiles);
+
+ IndexFileMeta dynamicBucketIndex = null;
+ if (request.scanDynamicBucketIndex()) {
+ dynamicBucketIndex =
+ indexFileHandler.scanHashIndex(snapshot, partition, bucket).orElse(null);
+ }
+
+ List<IndexFileMeta> deleteVectorsIndex = null;
+ if (request.scanDeleteVectorsIndex()) {
+ deleteVectorsIndex =
+ indexFileHandler.scan(snapshot, DELETION_VECTORS_INDEX, partition, bucket);
+ }
+
+ return new ScanCoordinationResponse(
+ snapshot, totalBuckets, restoreFiles, dynamicBucketIndex, deleteVectorsIndex);
+ }
+
+ public synchronized long latestCommittedIdentifier(String user) {
+ return latestCommittedIdentifiers.computeIfAbsent(user, this::computeLatestIdentifier);
+ }
+
+ private synchronized long computeLatestIdentifier(String user) {
+ Optional<Snapshot> snapshotOptional = table.snapshotManager().latestSnapshotOfUser(user);
+ if (!snapshotOptional.isPresent()) {
+ return Long.MIN_VALUE;
+ }
+
+ Snapshot latestSnapshotOfUser = snapshotOptional.get();
+ if (snapshot == null || latestSnapshotOfUser.id() > snapshot.id()) {
+ snapshot = latestSnapshotOfUser;
Review Comment:
You should also set this `snapshot` into `scan`.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: issues-unsubscribe@paimon.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] [core][flink] Introduce write operator coordinator to optimize write restore [paimon]
Posted by "tsreaper (via GitHub)" <gi...@apache.org>.
tsreaper commented on PR #5787:
URL: https://github.com/apache/paimon/pull/5787#issuecomment-2995355568
`latestSnapshotOfUserFromFilesystem` in `AbstractFileStoreWrite.createConflictAwareWriterCleanChecker` is also costly. Move it into the coordinator and make sture that `SnapshotManager` is removed from `AbstractFileStoreWrite`.
Also, the coordinator needs to take index files into consideration. Index file scanning happens in `IndexFileHandler`. `SnapshotManager` should also be removed from this class.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: issues-unsubscribe@paimon.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] [core][flink] Introduce write operator coordinator to optimize write restore [paimon]
Posted by "JingsongLi (via GitHub)" <gi...@apache.org>.
JingsongLi commented on code in PR #5787:
URL: https://github.com/apache/paimon/pull/5787#discussion_r2161361752
##########
paimon-flink/paimon-flink-common/src/main/java/org/apache/paimon/flink/FlinkConnectorOptions.java:
##########
@@ -523,6 +523,20 @@ public class FlinkConnectorOptions {
"Commit listener will be called after a successful commit. This option list custom commit "
+ "listener identifiers separated by comma.");
+ public static final ConfigOption<Boolean> SINK_WRITER_COORDINATOR_ENABLED =
+ key("sink.writer-coordinator.enabled")
+ .booleanType()
+ .defaultValue(false)
Review Comment:
This is just experimental feature.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: issues-unsubscribe@paimon.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] [core][flink] Introduce write operator coordinator to optimize write restore [paimon]
Posted by "tsreaper (via GitHub)" <gi...@apache.org>.
tsreaper commented on code in PR #5787:
URL: https://github.com/apache/paimon/pull/5787#discussion_r2162967591
##########
paimon-core/src/main/java/org/apache/paimon/operation/write/FileSystemWriteRestore.java:
##########
@@ -0,0 +1,99 @@
+/*
+ * 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.operation.write;
Review Comment:
No need for a new package. There are many other classes related to write. Why don't you put them all into this package?
##########
paimon-core/src/main/java/org/apache/paimon/operation/write/RestoreFiles.java:
##########
@@ -0,0 +1,78 @@
+/*
+ * 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.operation.write;
+
+import org.apache.paimon.Snapshot;
+import org.apache.paimon.index.IndexFileMeta;
+import org.apache.paimon.io.DataFileMeta;
+
+import javax.annotation.Nullable;
+
+import java.util.List;
+
+/** Restored files with snapshot and total buckets. */
+public class RestoreFiles {
+
+ private final @Nullable Snapshot snapshot;
+ private final @Nullable Integer totalBuckets;
+ private final @Nullable List<DataFileMeta> dataFiles;
+ private final @Nullable IndexFileMeta dynamicBucketIndex;
+ private final @Nullable List<IndexFileMeta> deleteVectorsIndex;
+
+ public RestoreFiles(
+ @Nullable Snapshot snapshot,
+ @Nullable Integer totalBuckets,
+ @Nullable List<DataFileMeta> dataFiles,
+ @Nullable IndexFileMeta dynamicBucketIndex,
+ @Nullable List<IndexFileMeta> deleteVectorsIndex) {
+ this.snapshot = snapshot;
+ this.totalBuckets = totalBuckets;
+ this.dataFiles = dataFiles;
+ this.dynamicBucketIndex = dynamicBucketIndex;
+ this.deleteVectorsIndex = deleteVectorsIndex;
+ }
+
+ @Nullable
+ public Snapshot snapshot() {
+ return snapshot;
+ }
+
+ @Nullable
+ public Integer totalBuckets() {
+ return totalBuckets;
+ }
+
+ public List<DataFileMeta> dataFiles() {
Review Comment:
`@Nullable`
##########
paimon-flink/paimon-flink-common/src/main/java/org/apache/paimon/flink/sink/coordinator/WriteOperatorCoordinator.java:
##########
@@ -0,0 +1,228 @@
+/*
+ * 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.coordinator;
+
+import org.apache.paimon.CoreOptions;
+import org.apache.paimon.Snapshot;
+import org.apache.paimon.data.BinaryRow;
+import org.apache.paimon.flink.sink.TableWriteOperator;
+import org.apache.paimon.fs.Path;
+import org.apache.paimon.index.IndexFileHandler;
+import org.apache.paimon.index.IndexFileMeta;
+import org.apache.paimon.io.DataFileMeta;
+import org.apache.paimon.manifest.ManifestEntry;
+import org.apache.paimon.operation.FileStoreScan;
+import org.apache.paimon.operation.write.WriteRestore;
+import org.apache.paimon.options.MemorySize;
+import org.apache.paimon.table.FileStoreTable;
+import org.apache.paimon.utils.SegmentsCache;
+
+import org.apache.flink.runtime.jobgraph.OperatorID;
+import org.apache.flink.runtime.operators.coordination.CoordinationRequest;
+import org.apache.flink.runtime.operators.coordination.CoordinationRequestHandler;
+import org.apache.flink.runtime.operators.coordination.CoordinationResponse;
+import org.apache.flink.runtime.operators.coordination.OperatorCoordinator;
+import org.apache.flink.runtime.operators.coordination.OperatorEvent;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ThreadPoolExecutor;
+
+import static org.apache.paimon.deletionvectors.DeletionVectorsIndexFile.DELETION_VECTORS_INDEX;
+import static org.apache.paimon.flink.FlinkConnectorOptions.SINK_WRITER_COORDINATOR_CACHE_MEMORY;
+import static org.apache.paimon.utils.SerializationUtils.deserializeBinaryRow;
+import static org.apache.paimon.utils.ThreadPoolUtils.createCachedThreadPool;
+
+/**
+ * {@link OperatorCoordinator} for {@link TableWriteOperator}, to use a single point to obtain the
+ * list of initialization files required for write operators.
+ */
+public class WriteOperatorCoordinator implements OperatorCoordinator, CoordinationRequestHandler {
+
+ private final FileStoreTable table;
+
+ private ThreadPoolExecutor executor;
+ private Map<String, Long> latestCommittedIdentifiers;
+
+ private volatile Snapshot snapshot;
+ private volatile FileStoreScan dataFileScan;
+ private volatile IndexFileHandler indexFileHandler;
+
+ public WriteOperatorCoordinator(FileStoreTable table) {
+ this.table = table;
+ }
+
+ private synchronized void refreshOrCreateScan() {
+ Optional<Snapshot> latestSnapshot = table.latestSnapshot();
+ if (!latestSnapshot.isPresent()) {
+ return;
+ }
+ if (dataFileScan == null) {
+ dataFileScan = table.store().newScan();
+ if (table.coreOptions().manifestDeleteFileDropStats()) {
+ dataFileScan.dropStats();
+ }
+ }
+ if (indexFileHandler == null) {
+ indexFileHandler = table.store().newIndexFileHandler();
+ }
+ snapshot = latestSnapshot.get();
+ dataFileScan.withSnapshot(snapshot);
+ }
+
+ private synchronized ScanCoordinationResponse scanDataFiles(ScanCoordinationRequest request)
+ throws IOException {
+ if (snapshot == null) {
+ return new ScanCoordinationResponse(null, null, null, null, null);
+ }
+
+ BinaryRow partition = deserializeBinaryRow(request.partition());
+ int bucket = request.bucket();
+
+ List<DataFileMeta> restoreFiles = new ArrayList<>();
+ List<ManifestEntry> entries =
+ dataFileScan.withPartitionBucket(partition, bucket).plan().files();
+ Integer totalBuckets = WriteRestore.extractDataFiles(entries, restoreFiles);
+
+ IndexFileMeta dynamicBucketIndex = null;
+ if (request.scanDynamicBucketIndex()) {
+ dynamicBucketIndex =
+ indexFileHandler.scanHashIndex(snapshot, partition, bucket).orElse(null);
+ }
+
+ List<IndexFileMeta> deleteVectorsIndex = null;
+ if (request.scanDeleteVectorsIndex()) {
+ deleteVectorsIndex =
+ indexFileHandler.scan(snapshot, DELETION_VECTORS_INDEX, partition, bucket);
+ }
+
+ return new ScanCoordinationResponse(
+ snapshot, totalBuckets, restoreFiles, dynamicBucketIndex, deleteVectorsIndex);
+ }
+
+ private synchronized LatestIdentifierResponse latestCommittedIdentifier(
+ LatestIdentifierRequest request) {
+ String user = request.user();
+ long identifier =
+ latestCommittedIdentifiers.computeIfAbsent(
+ user,
+ k ->
+ table.snapshotManager()
+ .latestSnapshotOfUser(user)
+ .map(Snapshot::commitIdentifier)
+ .orElse(Long.MIN_VALUE));
+ return new LatestIdentifierResponse(identifier);
+ }
Review Comment:
`snapshot` must be at least as new as `latestSnapshotOfUser`. If `latestSnapshotOfUser` is updated, `snapshot` and `dataFileScan` must also be updated, otherwise there will be consistency problems.
##########
paimon-core/src/main/java/org/apache/paimon/operation/AbstractFileStoreWrite.java:
##########
@@ -94,20 +98,19 @@ public abstract class AbstractFileStoreWrite<T> implements FileStoreWrite<T> {
protected AbstractFileStoreWrite(
SnapshotManager snapshotManager,
FileStoreScan scan,
- @Nullable IndexMaintainer.Factory<T> indexFactory,
+ @Nullable DynamicBucketIndexMaintainer.Factory dbMaintainerFactory,
@Nullable DeletionVectorsMaintainer.Factory dvMaintainerFactory,
String tableName,
CoreOptions options,
RowType partitionType) {
- this.snapshotManager = snapshotManager;
- this.scan = scan;
- // Statistic is useless in writer
- if (options.manifestDeleteFileDropStats()) {
- if (this.scan != null) {
- this.scan.dropStats();
- }
+ IndexFileHandler indexFileHandler = null;
+ if (dbMaintainerFactory != null) {
+ indexFileHandler = dbMaintainerFactory.indexFileHandler();
+ } else if (dvMaintainerFactory != null) {
+ indexFileHandler = dvMaintainerFactory.indexFileHandler();
}
- this.indexFactory = indexFactory;
+ this.restore = new FileSystemWriteRestore(options, snapshotManager, scan, indexFileHandler);
Review Comment:
Set `FileSystemWriteRestore` in `newWrite` methods of `FileStore`s. Remove `scan` and `snapshotManager` from the constructor. Some writers (like unaware bucket writers for append only tables) don't need scan.
Also consider removing `dbMaintainerFactory` and `dvMaintainerFactory` from the constructor? Many file stores also don't need them.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: issues-unsubscribe@paimon.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org