This is an automated email from the ASF dual-hosted git repository.

ahmedabu98 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/beam.git


The following commit(s) were added to refs/heads/master by this push:
     new e4779cf79f2 Fix flaky FileIOTest.testMatchWatchForNewFiles test under 
CI filesystems (#38047)
e4779cf79f2 is described below

commit e4779cf79f22a55cbbe69ae3c743caba67a2cf64
Author: Bruno Volpato <[email protected]>
AuthorDate: Mon Aug 3 02:53:40 2026 -0400

    Fix flaky FileIOTest.testMatchWatchForNewFiles test under CI filesystems 
(#38047)
    
    * Fix flaky FileIOTest.testMatchWatchForNewFiles test under CI filesystems
    
    addresses #19480
    
    The `FileIOTest.testMatchWatchForNewFiles` test occasionally flakes in the 
CI environment because the `updOptions` configuration in `CopyFilesFn` does not 
preserve file attributes when overwriting existing files. In some CI 
filesystems, this causes the copied file to register a `lastModifiedMillis` 
timestamp of `0`. Thus, when `ExtractFilenameAndLastUpdateFn` parses this file, 
it throws a `RuntimeException` at `FileIO.java:800`, failing the pipeline run.
    
    This PR adds `StandardCopyOption.COPY_ATTRIBUTES` to preserve the file's 
original timestamps, avoiding the exception.
    
    * Stabilize FileIOTest updated-file timestamp assertions
---
 .../java/org/apache/beam/sdk/io/FileIOTest.java    | 22 +++++++++++++++++++---
 1 file changed, 19 insertions(+), 3 deletions(-)

diff --git 
a/sdks/java/core/src/test/java/org/apache/beam/sdk/io/FileIOTest.java 
b/sdks/java/core/src/test/java/org/apache/beam/sdk/io/FileIOTest.java
index 2cffce76213..c5a227d46b8 100644
--- a/sdks/java/core/src/test/java/org/apache/beam/sdk/io/FileIOTest.java
+++ b/sdks/java/core/src/test/java/org/apache/beam/sdk/io/FileIOTest.java
@@ -232,9 +232,10 @@ public class FileIOTest implements Serializable {
   /** DoFn that copy test files from source to watch path. */
   private static class CopyFilesFn
       extends DoFn<KV<String, MatchResult.Metadata>, MatchResult.Metadata> {
-    public CopyFilesFn(Path sourcePath, Path watchPath) {
+    public CopyFilesFn(Path sourcePath, Path watchPath, long 
baseTimestampMillis) {
       this.sourcePathStr = sourcePath.toString();
       this.watchPathStr = watchPath.toString();
+      this.baseTimestampMillis = baseTimestampMillis;
     }
 
     @StateId("count")
@@ -249,16 +250,24 @@ public class FileIOTest implements Serializable {
       context.output(Objects.requireNonNull(context.element()).getValue());
 
       CopyOption[] cpOptions = {StandardCopyOption.COPY_ATTRIBUTES};
-      CopyOption[] updOptions = {StandardCopyOption.REPLACE_EXISTING};
+      CopyOption[] updOptions = {
+        StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.COPY_ATTRIBUTES
+      };
       final Path sourcePath = Paths.get(sourcePathStr);
       final Path watchPath = Paths.get(watchPathStr);
 
       if (0 == current) {
         Thread.sleep(100);
+        // Ensure overwrite updates get a distinct mtime even when 
COPY_ATTRIBUTES is enabled.
+        Files.setLastModifiedTime(
+            sourcePath.resolve("first"), 
FileTime.fromMillis(baseTimestampMillis + 2000));
         Files.copy(sourcePath.resolve("first"), watchPath.resolve("first"), 
updOptions);
         Files.copy(sourcePath.resolve("second"), watchPath.resolve("second"), 
cpOptions);
       } else if (1 == current) {
         Thread.sleep(100);
+        FileTime updateTime = FileTime.fromMillis(baseTimestampMillis + 4000);
+        Files.setLastModifiedTime(sourcePath.resolve("first"), updateTime);
+        Files.setLastModifiedTime(sourcePath.resolve("second"), updateTime);
         Files.copy(sourcePath.resolve("first"), watchPath.resolve("first"), 
updOptions);
         Files.copy(sourcePath.resolve("second"), watchPath.resolve("second"), 
updOptions);
         Files.copy(sourcePath.resolve("third"), watchPath.resolve("third"), 
cpOptions);
@@ -269,6 +278,7 @@ public class FileIOTest implements Serializable {
     // Member variables need to be serializable.
     private final String sourcePathStr;
     private final String watchPathStr;
+    private final long baseTimestampMillis;
   }
 
   private static class AfterNumberOfNewOutputs
@@ -318,6 +328,12 @@ public class FileIOTest implements Serializable {
     Files.write(sourcePath.resolve("first"), new byte[42]);
     Files.write(sourcePath.resolve("second"), new byte[37]);
     Files.write(sourcePath.resolve("third"), new byte[99]);
+    // Keep controlled mtimes in the past so updates are distinct without 
future timestamps.
+    long baseTimestampMillis = System.currentTimeMillis() - 
Duration.standardMinutes(1).getMillis();
+    FileTime baseTimestamp = FileTime.fromMillis(baseTimestampMillis);
+    Files.setLastModifiedTime(sourcePath.resolve("first"), baseTimestamp);
+    Files.setLastModifiedTime(sourcePath.resolve("second"), baseTimestamp);
+    Files.setLastModifiedTime(sourcePath.resolve("third"), baseTimestamp);
 
     // Create a "watch" directory that the pipeline will copy files into.
     final Path watchPath = tmpFolder.getRoot().toPath().resolve("watch");
@@ -380,7 +396,7 @@ public class FileIOTest implements Serializable {
                             TypeDescriptors.strings(),
                             TypeDescriptor.of(MatchResult.Metadata.class)))
                     .via((metadata) -> KV.of("dumb key", metadata)))
-            .apply(ParDo.of(new CopyFilesFn(sourcePath, watchPath)));
+            .apply(ParDo.of(new CopyFilesFn(sourcePath, watchPath, 
baseTimestampMillis)));
 
     assertEquals(PCollection.IsBounded.UNBOUNDED, matchMetadata.isBounded());
     assertEquals(PCollection.IsBounded.UNBOUNDED, 
matchAllMetadata.isBounded());

Reply via email to