kevinjqliu commented on code in PR #15470:
URL: https://github.com/apache/iceberg/pull/15470#discussion_r3447082030


##########
spark/v4.1/spark/src/main/java/org/apache/iceberg/spark/actions/RewriteTablePathSparkAction.java:
##########
@@ -682,27 +704,120 @@ private static RewriteResult<DeleteFile> 
writeDeleteManifest(
           specsById,
           sourcePrefix,
           targetPrefix,
-          stagingLocation);
+          stagingLocation,
+          rewrittenDeleteFileSizes.value());
     } catch (IOException e) {
       throw new RuntimeIOException(e);
     }
   }
 
-  private void rewritePositionDeletes(Set<DeleteFile> toRewrite) {
+  /**
+   * Enumerate the distinct position delete files referenced by the delete 
manifests being
+   * rewritten. Equality delete files are excluded because they hold no 
absolute paths and are not
+   * rewritten.
+   */
+  private Set<DeleteFile> positionDeletesToRewrite(Set<ManifestFile> 
metaFiles) {
+    Set<ManifestFile> deleteManifests =
+        metaFiles.stream()
+            .filter(manifest -> manifest.content() == ManifestContent.DELETES)
+            .collect(Collectors.toSet());
+    if (deleteManifests.isEmpty()) {
+      return Collections.emptySet();
+    }
+
+    Encoder<ManifestFile> manifestFileEncoder = 
Encoders.javaSerialization(ManifestFile.class);
+    Dataset<ManifestFile> manifestDS =
+        spark().createDataset(Lists.newArrayList(deleteManifests), 
manifestFileEncoder);
+    Encoder<DeleteFile> deleteFileEncoder = 
Encoders.javaSerialization(DeleteFile.class);
+
+    List<DeleteFile> referencedDeleteFiles =
+        manifestDS
+            .repartition(deleteManifests.size())
+            .flatMap(positionDeletesInManifest(tableBroadcast()), 
deleteFileEncoder)
+            .collectAsList();
+
+    // DeleteFile does not override equals(); DeleteFileSet dedupes by path so 
a delete file shared
+    // across manifests is rewritten only once.
+    DeleteFileSet distinct = DeleteFileSet.create();
+    distinct.addAll(referencedDeleteFiles);
+    return distinct;
+  }
+
+  private static FlatMapFunction<ManifestFile, DeleteFile> 
positionDeletesInManifest(
+      Broadcast<Table> tableArg) {
+    return manifestFile -> {
+      Table table = tableArg.getValue();
+      List<DeleteFile> deleteFiles = Lists.newArrayList();
+      try (ManifestReader<DeleteFile> reader =
+          ManifestFiles.readDeleteManifest(manifestFile, table.io(), 
table.specs())) {
+        for (DeleteFile deleteFile : reader) {
+          if (deleteFile.content() == FileContent.POSITION_DELETES) {
+            deleteFiles.add(deleteFile.copy());
+          }
+        }
+      }
+      return deleteFiles.iterator();
+    };
+  }
+
+  /**
+   * Rewrite the given position delete files in parallel, returning a map from 
each source delete
+   * file path to the size of its rewritten file.
+   */
+  private Map<String, Long> rewritePositionDeletes(Set<DeleteFile> toRewrite) {
     if (toRewrite.isEmpty()) {
-      return;
+      return Collections.emptyMap();
     }
 
     Encoder<DeleteFile> deleteFileEncoder = 
Encoders.javaSerialization(DeleteFile.class);
-    Dataset<DeleteFile> deleteFileDs =
+    Dataset<DeleteFile> deleteFileDS =
         spark().createDataset(Lists.newArrayList(toRewrite), 
deleteFileEncoder);
 
     PositionDeleteReaderWriter posDeleteReaderWriter = new 
SparkPositionDeleteReaderWriter();
-    deleteFileDs
-        .repartition(toRewrite.size())
-        .foreach(
-            rewritePositionDelete(
-                tableBroadcast(), sourcePrefix, targetPrefix, stagingDir, 
posDeleteReaderWriter));
+    List<Tuple2<String, Long>> rewrittenSizes =
+        deleteFileDS
+            .repartition(toRewrite.size())
+            .map(
+                rewritePositionDelete(
+                    tableBroadcast(),
+                    sourcePrefix,
+                    targetPrefix,
+                    stagingDir,
+                    posDeleteReaderWriter),
+                Encoders.tuple(Encoders.STRING(), Encoders.LONG()))
+            .collectAsList();
+
+    Map<String, Long> sizesBySourcePath = Maps.newHashMap();
+    for (Tuple2<String, Long> entry : rewrittenSizes) {
+      sizesBySourcePath.put(entry._1(), entry._2());
+    }
+    return sizesBySourcePath;
+  }
+
+  private static MapFunction<DeleteFile, Tuple2<String, Long>> 
rewritePositionDelete(
+      Broadcast<Table> tableArg,
+      String sourcePrefixArg,
+      String targetPrefixArg,
+      String stagingLocationArg,
+      PositionDeleteReaderWriter posDeleteReaderWriter) {
+    return deleteFile -> {
+      FileIO io = tableArg.getValue().io();
+      String newPath =
+          RewriteTablePathUtil.stagingPath(
+              deleteFile.location(), sourcePrefixArg, stagingLocationArg);
+      OutputFile outputFile = io.newOutputFile(newPath);
+      PartitionSpec spec = 
tableArg.getValue().specs().get(deleteFile.specId());
+      long rewrittenLength =
+          RewriteTablePathUtil.rewritePositionDeleteFileReturningLength(
+              deleteFile,
+              outputFile,
+              io,
+              spec,
+              sourcePrefixArg,
+              targetPrefixArg,
+              posDeleteReaderWriter);
+      return new Tuple2<>(deleteFile.location(), rewrittenLength);

Review Comment:
   for context:
   * `positionDeletesToRewrite` can return two `DeleteFiles` with the same 
location, as long as they have different DV blob offsets/sizes
   * `rewritePositionDelete` writes a new file per `DeleteFile`
   * so the first will write to disk, second will fail with 
`AlreadyExistingException`



-- 
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: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to