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/paimon.git


The following commit(s) were added to refs/heads/master by this push:
     new 3dcc5ae50 [core] Support parallel reading of local orphan clean (#4320)
3dcc5ae50 is described below

commit 3dcc5ae509c14889cbea7074e065d5503600a72e
Author: bknbkn <[email protected]>
AuthorDate: Thu Oct 17 12:14:34 2024 +0800

    [core] Support parallel reading of local orphan clean (#4320)
---
 .../paimon/operation/LocalOrphanFilesClean.java    | 66 ++++++++++++++-------
 .../apache/paimon/operation/OrphanFilesClean.java  |  8 ---
 .../org/apache/paimon/utils/SnapshotManager.java   | 67 +++++++++++++++-------
 3 files changed, 92 insertions(+), 49 deletions(-)

diff --git 
a/paimon-core/src/main/java/org/apache/paimon/operation/LocalOrphanFilesClean.java
 
b/paimon-core/src/main/java/org/apache/paimon/operation/LocalOrphanFilesClean.java
index f3f58107a..3ee108c10 100644
--- 
a/paimon-core/src/main/java/org/apache/paimon/operation/LocalOrphanFilesClean.java
+++ 
b/paimon-core/src/main/java/org/apache/paimon/operation/LocalOrphanFilesClean.java
@@ -40,18 +40,21 @@ import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 import java.util.concurrent.Future;
 import java.util.concurrent.ThreadPoolExecutor;
 import java.util.concurrent.TimeUnit;
+import java.util.function.Consumer;
 import java.util.function.Function;
 import java.util.stream.Collectors;
 
 import static org.apache.paimon.utils.Preconditions.checkArgument;
 import static org.apache.paimon.utils.ThreadPoolUtils.createCachedThreadPool;
 import static org.apache.paimon.utils.ThreadPoolUtils.randomlyExecute;
+import static org.apache.paimon.utils.ThreadPoolUtils.randomlyOnlyExecute;
 
 /**
  * Local {@link OrphanFilesClean}, it will use thread pool to execute deletion.
@@ -113,31 +116,52 @@ public class LocalOrphanFilesClean extends 
OrphanFilesClean {
         return deleteFiles;
     }
 
-    private List<String> getUsedFiles(String branch) {
-        List<String> usedFiles = new ArrayList<>();
+    private void collectWithoutDataFile(
+            String branch, Consumer<String> usedFileConsumer, Consumer<String> 
manifestConsumer)
+            throws IOException {
+        randomlyOnlyExecute(
+                executor,
+                snapshot -> {
+                    try {
+                        collectWithoutDataFile(
+                                branch, snapshot, usedFileConsumer, 
manifestConsumer);
+                    } catch (IOException e) {
+                        throw new RuntimeException(e);
+                    }
+                },
+                safelyGetAllSnapshots(branch));
+    }
+
+    private Set<String> getUsedFiles(String branch) {
+        Set<String> usedFiles = ConcurrentHashMap.newKeySet();
         ManifestFile manifestFile =
                 
table.switchToBranch(branch).store().manifestFileFactory().create();
         try {
-            Set<String> manifests = new HashSet<>();
+            Set<String> manifests = ConcurrentHashMap.newKeySet();
             collectWithoutDataFile(branch, usedFiles::add, manifests::add);
-            List<String> dataFiles = new ArrayList<>();
-            for (String manifestName : manifests) {
-                retryReadingFiles(
-                                () -> 
manifestFile.readWithIOException(manifestName),
-                                Collections.<ManifestEntry>emptyList())
-                        .stream()
-                        .map(ManifestEntry::file)
-                        .forEach(
-                                f -> {
-                                    if 
(candidateDeletes.contains(f.fileName())) {
-                                        dataFiles.add(f.fileName());
-                                    }
-                                    f.extraFiles().stream()
-                                            .filter(candidateDeletes::contains)
-                                            .forEach(dataFiles::add);
-                                });
-            }
-            usedFiles.addAll(dataFiles);
+            randomlyOnlyExecute(
+                    executor,
+                    manifestName -> {
+                        try {
+                            retryReadingFiles(
+                                            () -> 
manifestFile.readWithIOException(manifestName),
+                                            
Collections.<ManifestEntry>emptyList())
+                                    .stream()
+                                    .map(ManifestEntry::file)
+                                    .forEach(
+                                            f -> {
+                                                if 
(candidateDeletes.contains(f.fileName())) {
+                                                    
usedFiles.add(f.fileName());
+                                                }
+                                                f.extraFiles().stream()
+                                                        
.filter(candidateDeletes::contains)
+                                                        
.forEach(usedFiles::add);
+                                            });
+                        } catch (IOException e) {
+                            throw new RuntimeException(e);
+                        }
+                    },
+                    manifests);
         } catch (IOException e) {
             throw new RuntimeException(e);
         }
diff --git 
a/paimon-core/src/main/java/org/apache/paimon/operation/OrphanFilesClean.java 
b/paimon-core/src/main/java/org/apache/paimon/operation/OrphanFilesClean.java
index 0f2bad27f..5698908cb 100644
--- 
a/paimon-core/src/main/java/org/apache/paimon/operation/OrphanFilesClean.java
+++ 
b/paimon-core/src/main/java/org/apache/paimon/operation/OrphanFilesClean.java
@@ -146,14 +146,6 @@ public abstract class OrphanFilesClean implements 
Serializable {
         return readSnapshots;
     }
 
-    protected void collectWithoutDataFile(
-            String branch, Consumer<String> usedFileConsumer, Consumer<String> 
manifestConsumer)
-            throws IOException {
-        for (Snapshot snapshot : safelyGetAllSnapshots(branch)) {
-            collectWithoutDataFile(branch, snapshot, usedFileConsumer, 
manifestConsumer);
-        }
-    }
-
     protected void collectWithoutDataFile(
             String branch,
             Snapshot snapshot,
diff --git 
a/paimon-core/src/main/java/org/apache/paimon/utils/SnapshotManager.java 
b/paimon-core/src/main/java/org/apache/paimon/utils/SnapshotManager.java
index 2dccc8250..7e2fce0ad 100644
--- a/paimon-core/src/main/java/org/apache/paimon/utils/SnapshotManager.java
+++ b/paimon-core/src/main/java/org/apache/paimon/utils/SnapshotManager.java
@@ -42,8 +42,10 @@ import java.util.Iterator;
 import java.util.List;
 import java.util.Optional;
 import java.util.Set;
+import java.util.concurrent.ExecutorService;
 import java.util.concurrent.TimeUnit;
 import java.util.function.BinaryOperator;
+import java.util.function.Consumer;
 import java.util.function.Function;
 import java.util.function.Predicate;
 import java.util.stream.Collectors;
@@ -52,6 +54,8 @@ import java.util.stream.LongStream;
 import static org.apache.paimon.utils.BranchManager.DEFAULT_MAIN_BRANCH;
 import static org.apache.paimon.utils.BranchManager.branchPath;
 import static org.apache.paimon.utils.FileUtils.listVersionedFiles;
+import static org.apache.paimon.utils.ThreadPoolUtils.createCachedThreadPool;
+import static org.apache.paimon.utils.ThreadPoolUtils.randomlyOnlyExecute;
 
 /** Manager for {@link Snapshot}, providing utility methods related to paths 
and snapshot hints. */
 public class SnapshotManager implements Serializable {
@@ -414,8 +418,7 @@ public class SnapshotManager implements Serializable {
     }
 
     public Iterator<Snapshot> snapshotsWithinRange(
-            Optional<Long> optionalMaxSnapshotId, Optional<Long> 
optionalMinSnapshotId)
-            throws IOException {
+            Optional<Long> optionalMaxSnapshotId, Optional<Long> 
optionalMinSnapshotId) {
         Long lowerBoundSnapshotId = earliestSnapshotId();
         Long upperBoundSnapshotId = latestSnapshotId();
         Long lowerId;
@@ -457,7 +460,7 @@ public class SnapshotManager implements Serializable {
 
     public Iterator<Changelog> changelogs() throws IOException {
         return listVersionedFiles(fileIO, changelogDirectory(), 
CHANGELOG_PREFIX)
-                .map(snapshotId -> changelog(snapshotId))
+                .map(this::changelog)
                 .sorted(Comparator.comparingLong(Changelog::id))
                 .iterator();
     }
@@ -469,16 +472,21 @@ public class SnapshotManager implements Serializable {
     public List<Snapshot> safelyGetAllSnapshots() throws IOException {
         List<Path> paths =
                 listVersionedFiles(fileIO, snapshotDirectory(), 
SNAPSHOT_PREFIX)
-                        .map(id -> snapshotPath(id))
+                        .map(this::snapshotPath)
                         .collect(Collectors.toList());
 
-        List<Snapshot> snapshots = new ArrayList<>();
-        for (Path path : paths) {
-            try {
-                snapshots.add(Snapshot.fromJson(fileIO.readFileUtf8(path)));
-            } catch (FileNotFoundException ignored) {
-            }
-        }
+        List<Snapshot> snapshots = Collections.synchronizedList(new 
ArrayList<>(paths.size()));
+        collectSnapshots(
+                path -> {
+                    try {
+                        
snapshots.add(Snapshot.fromJson(fileIO.readFileUtf8(path)));
+                    } catch (IOException e) {
+                        if (!(e instanceof FileNotFoundException)) {
+                            throw new RuntimeException(e);
+                        }
+                    }
+                },
+                paths);
 
         return snapshots;
     }
@@ -486,21 +494,40 @@ public class SnapshotManager implements Serializable {
     public List<Changelog> safelyGetAllChangelogs() throws IOException {
         List<Path> paths =
                 listVersionedFiles(fileIO, changelogDirectory(), 
CHANGELOG_PREFIX)
-                        .map(id -> longLivedChangelogPath(id))
+                        .map(this::longLivedChangelogPath)
                         .collect(Collectors.toList());
 
-        List<Changelog> changelogs = new ArrayList<>();
-        for (Path path : paths) {
-            try {
-                String json = fileIO.readFileUtf8(path);
-                changelogs.add(Changelog.fromJson(json));
-            } catch (FileNotFoundException ignored) {
-            }
-        }
+        List<Changelog> changelogs = Collections.synchronizedList(new 
ArrayList<>(paths.size()));
+        collectSnapshots(
+                path -> {
+                    try {
+                        
changelogs.add(Changelog.fromJson(fileIO.readFileUtf8(path)));
+                    } catch (IOException e) {
+                        if (!(e instanceof FileNotFoundException)) {
+                            throw new RuntimeException(e);
+                        }
+                    }
+                },
+                paths);
 
         return changelogs;
     }
 
+    private void collectSnapshots(Consumer<Path> pathConsumer, List<Path> 
paths)
+            throws IOException {
+        ExecutorService executor =
+                createCachedThreadPool(
+                        Runtime.getRuntime().availableProcessors(), 
"SNAPSHOT_COLLECTOR");
+
+        try {
+            randomlyOnlyExecute(executor, pathConsumer, paths);
+        } catch (RuntimeException e) {
+            throw new IOException(e);
+        } finally {
+            executor.shutdown();
+        }
+    }
+
     /**
      * Try to get non snapshot files. If any error occurred, just ignore it 
and return an empty
      * result.

Reply via email to