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

dataroaring pushed a commit to branch branch-3.0
in repository https://gitbox.apache.org/repos/asf/doris.git


The following commit(s) were added to refs/heads/branch-3.0 by this push:
     new ea8d0d8c33e branch-3.0: [fix](hudi) replace non thread safe 
SimpleDateFormat #48923 (#49021)
ea8d0d8c33e is described below

commit ea8d0d8c33e8dc91d19996731a2c4c6a5f084a7e
Author: github-actions[bot] 
<41898282+github-actions[bot]@users.noreply.github.com>
AuthorDate: Sat Mar 22 17:04:24 2025 +0800

    branch-3.0: [fix](hudi) replace non thread safe SimpleDateFormat #48923 
(#49021)
    
    Cherry-picked from #48923
    
    Co-authored-by: Mingyu Chen (Rayner) <morning...@163.com>
---
 .../apache/doris/datasource/hudi/HudiUtils.java    |  8 +-
 .../doris/datasource/hudi/HudiUtilsTest.java       | 97 ++++++++++++++++++++++
 2 files changed, 102 insertions(+), 3 deletions(-)

diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/datasource/hudi/HudiUtils.java 
b/fe/fe-core/src/main/java/org/apache/doris/datasource/hudi/HudiUtils.java
index 889ca4b5418..0e8d737937a 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/datasource/hudi/HudiUtils.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/datasource/hudi/HudiUtils.java
@@ -45,14 +45,15 @@ import org.apache.hudi.common.util.Option;
 import org.apache.hudi.storage.hadoop.HadoopStorageConfiguration;
 
 import java.text.ParseException;
-import java.text.SimpleDateFormat;
+import java.time.LocalDate;
+import java.time.format.DateTimeFormatter;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Optional;
 import java.util.stream.Collectors;
 
 public class HudiUtils {
-    private static final SimpleDateFormat defaultDateFormat = new 
SimpleDateFormat("yyyy-MM-dd");
+    private static final DateTimeFormatter DEFAULT_DATE_FORMATTER = 
DateTimeFormatter.ofPattern("yyyy-MM-dd");
 
     /**
      * Convert different query instant time format to the commit time format.
@@ -74,7 +75,8 @@ public class HudiUtils {
             HoodieActiveTimeline.parseDateFromInstantTime(queryInstant); // 
validate the format
             return queryInstant;
         } else if (instantLength == 10) { // for yyyy-MM-dd
-            return 
HoodieActiveTimeline.formatDate(defaultDateFormat.parse(queryInstant));
+            LocalDate date = LocalDate.parse(queryInstant, 
DEFAULT_DATE_FORMATTER);
+            return 
HoodieActiveTimeline.formatDate(java.sql.Date.valueOf(date));
         } else {
             throw new IllegalArgumentException("Unsupported query instant time 
format: " + queryInstant
                     + ", Supported time format are: 'yyyy-MM-dd 
HH:mm:ss[.SSS]' "
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/datasource/hudi/HudiUtilsTest.java 
b/fe/fe-core/src/test/java/org/apache/doris/datasource/hudi/HudiUtilsTest.java
index d636f0bf84d..759653e7539 100644
--- 
a/fe/fe-core/src/test/java/org/apache/doris/datasource/hudi/HudiUtilsTest.java
+++ 
b/fe/fe-core/src/test/java/org/apache/doris/datasource/hudi/HudiUtilsTest.java
@@ -26,6 +26,8 @@ import mockit.Mock;
 import mockit.MockUp;
 import org.apache.hadoop.hive.metastore.api.StorageDescriptor;
 import org.apache.hadoop.hive.metastore.api.Table;
+import org.apache.hudi.common.table.timeline.HoodieActiveTimeline;
+import org.apache.hudi.common.table.timeline.HoodieInstantTimeGenerator;
 import org.junit.Assert;
 import org.junit.Test;
 
@@ -195,4 +197,99 @@ public class HudiUtilsTest {
         Assert.assertTrue(meta.delete());
         Files.delete(hudiTable);
     }
+
+    @Test
+    public void testFormatQueryInstantThreadSafety() throws Exception {
+        // Mock HoodieActiveTimeline and HoodieInstantTimeGenerator methods
+        new MockUp<HoodieInstantTimeGenerator>() {
+            @Mock
+            public String getInstantForDateString(String dateString) {
+                return "mocked_" + dateString.replace(" ", "_").replace(":", 
"_").replace(".", "_");
+            }
+        };
+
+        new MockUp<HoodieActiveTimeline>() {
+            @Mock
+            public void parseDateFromInstantTime(String instantTime) {
+                // Just a validation method, no return value needed
+            }
+
+            @Mock
+            public String formatDate(java.util.Date date) {
+                return "formatted_" + date.getTime();
+            }
+        };
+
+        // Test different date formats
+        String[] dateFormats = {
+                "2023-01-15",                 // yyyy-MM-dd format
+                "2023-01-15 14:30:25",        // yyyy-MM-dd HH:mm:ss format
+                "2023-01-15 14:30:25.123",    // yyyy-MM-dd HH:mm:ss.SSS format
+                "20230115143025",             // yyyyMMddHHmmss format
+                "20230115143025123"           // yyyyMMddHHmmssSSS format
+        };
+
+        // Single thread test for basic functionality
+        for (String dateFormat : dateFormats) {
+            String result = HudiUtils.formatQueryInstant(dateFormat);
+            Assert.assertNotNull(result);
+
+            // Verify expected format based on input length
+            if (dateFormat.length() == 10) { // yyyy-MM-dd
+                Assert.assertTrue(result.startsWith("formatted_"));
+            } else if (dateFormat.length() == 19 || dateFormat.length() == 23) 
{ // yyyy-MM-dd HH:mm:ss[.SSS]
+                Assert.assertTrue(result.startsWith("mocked_"));
+            } else {
+                // yyyyMMddHHmmss[SSS] passes through
+                Assert.assertEquals(dateFormat, result);
+            }
+        }
+
+        // Multi-thread test for thread safety
+        int threadCount = 10;
+        int iterationsPerThread = 100;
+
+        Thread[] threads = new Thread[threadCount];
+        Exception[] threadExceptions = new Exception[threadCount];
+
+        // Create a map to store expected results for each date format
+        final java.util.Map<String, String> expectedResults = new 
java.util.HashMap<>();
+        for (String dateFormat : dateFormats) {
+            expectedResults.put(dateFormat, 
HudiUtils.formatQueryInstant(dateFormat));
+        }
+
+        for (int i = 0; i < threadCount; i++) {
+            final int threadId = i;
+            threads[i] = new Thread(() -> {
+                try {
+                    for (int j = 0; j < iterationsPerThread; j++) {
+                        // Each thread cycles through all date formats
+                        String dateFormat = dateFormats[j % 
dateFormats.length];
+                        String result = 
HudiUtils.formatQueryInstant(dateFormat);
+
+                        // Verify the result matches the expected value for 
this date format
+                        String expected = expectedResults.get(dateFormat);
+                        Assert.assertEquals("Thread " + threadId + " iteration 
" + j
+                                        + " got incorrect result for format " 
+ dateFormat,
+                                expected, result);
+                    }
+                } catch (Exception e) {
+                    threadExceptions[threadId] = e;
+                }
+            });
+            threads[i].start();
+        }
+
+        // Wait for all threads to complete
+        for (Thread thread : threads) {
+            thread.join(5000); // Timeout after 5 seconds to ensure test 
doesn't run too long
+        }
+
+        // Check if any thread encountered exceptions
+        for (int i = 0; i < threadCount; i++) {
+            if (threadExceptions[i] != null) {
+                throw new AssertionError("Thread " + i + " failed with 
exception", threadExceptions[i]);
+            }
+        }
+    }
 }


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscr...@doris.apache.org
For additional commands, e-mail: commits-h...@doris.apache.org

Reply via email to