github-actions[bot] commented on code in PR #65219:
URL: https://github.com/apache/doris/pull/65219#discussion_r3614189953


##########
fe/fe-core/src/main/java/org/apache/doris/clone/DynamicPartitionScheduler.java:
##########
@@ -201,23 +199,48 @@ private static long getNextPartitionSize(List<Long> 
historyPartitionsSize) {
     }
 
     private static Pair<Integer, Integer> 
getBucketsNum(DynamicPartitionProperty property, OlapTable table,
-            String partitionName, String nowPartitionName, boolean 
executeFirstTime) {
+            String partitionName, String nowPartitionName, boolean 
executeFirstTime, String currentUtcBorder) {
         AutoBucketCalculator.AutoBucketContext context = new 
AutoBucketCalculator.AutoBucketContext(
-                table, partitionName, nowPartitionName, executeFirstTime, 
property.getBuckets());
+                table, partitionName, nowPartitionName, executeFirstTime, 
property.getBuckets(),
+                currentUtcBorder);
 
         AutoBucketCalculator.AutoBucketResult result = 
AutoBucketCalculator.calculateAutoBuckets(context);
         return Pair.of(result.getBuckets(), result.getPreviousBuckets());
     }
 
-    public static List<Partition> getHistoricalPartitions(OlapTable table, 
String nowPartitionName) {
+    /**
+     * Get all partitions except the current one. When currentUtcBorder is 
non-null,
+     * the current partition is identified by matching its range lower bound 
against
+     * currentUtcBorder rather than by name. For TIMESTAMPTZ tables both names 
and
+     * ranges are UTC-based, so the name-based fallback is also correct; the 
range
+     * check provides an additional safety layer.
+     */
+    public static List<Partition> getHistoricalPartitions(OlapTable table, 
String nowPartitionName,
+            String currentUtcBorder) {
         table.readLock();
         try {
             RangePartitionInfo info = (RangePartitionInfo) 
(table.getPartitionInfo());
             List<Map.Entry<Long, PartitionItem>> idToItems = new 
ArrayList<>(info.getIdToItem(false).entrySet());
             idToItems.sort(Comparator.comparing(o -> ((RangePartitionItem) 
o.getValue()).getItems().upperEndpoint()));
             return idToItems.stream()
                     .map(entry -> table.getPartition(entry.getKey()))
-                    .filter(partition -> partition != null && 
!partition.getName().equals(nowPartitionName))
+                    .filter(partition -> {
+                        if (partition == null) {
+                            return false;
+                        }
+                        // When currentUtcBorder is available, exclude the 
current
+                        // partition by comparing range lower bounds, not 
names.
+                        if (currentUtcBorder != null) {
+                            RangePartitionItem item = (RangePartitionItem) 
info.getItem(partition.getId());
+                            if (item != null) {
+                                PartitionKey lower = 
item.getItems().lowerEndpoint();
+                                if 
(lower.getKeys().get(0).getStringValue().equals(currentUtcBorder)) {

Review Comment:
   `currentUtcBorder` is formatted only to whole seconds, but a scaled temporal 
lower key preserves its declared scale in `getStringValue()` (for example, 
`2026-07-20 00:00:00.000000+00:00` for `TIMESTAMPTZ(6)` versus `2026-07-20 
00:00:00+00:00` here), so equal partition keys fail this string comparison. The 
name fallback is not guaranteed: changing the supported 
`dynamic_partition.prefix` updates the property without renaming existing 
partitions. On the next pass that creates a missing future partition, both 
checks then miss the active partition and `AutoBucketCalculator` can size from 
its partial data. Please compare a parsed/normalized `PartitionKey` (or pass 
the current key directly) and cover a scaled type with an existing old-prefix 
current partition.



##########
fe/fe-core/src/main/java/org/apache/doris/clone/DynamicPartitionScheduler.java:
##########
@@ -655,19 +679,14 @@ private ArrayList<DropPartitionOp> 
getDropPartitionOpForAutoPartition(Database d
         idToItems.sort(Comparator.comparing(o -> ((RangePartitionItem) 
o.getValue()).getItems().upperEndpoint()));
 
         // Get current time as PartitionKey for comparison
-        ZonedDateTime now = ZonedDateTime.now(DateUtils.getTimeZone());
         Column partitionColumn = info.getPartitionColumns().get(0);
+        boolean isTimestampTz = partitionColumn.getDataType() == 
PrimitiveType.TIMESTAMPTZ;
+        ZonedDateTime nowTz = ZonedDateTime.now(DateUtils.getTimeZone());
+        ZonedDateTime now = isTimestampTz ? 
nowTz.withZoneSameInstant(ZoneOffset.UTC) : nowTz;

Review Comment:
   For TIMESTAMPTZ, this converts the clock to UTC, but `currentTimeStr` below 
is formatted without an offset and `PartitionKey.createPartitionKey()` reparses 
it through `TimestampTzLiteral.fromSessionTimeZone()`. The daemon's 
null-context path happens to fall back to UTC, but 
`InternalCatalog.createOlapTable()` also invokes first-time scheduling inline 
with the creator's `ConnectContext`, after legal predefined AUTO RANGE 
partitions have been installed. In an `America/Chicago` session, actual 
`12:30Z` becomes suffix-less `12:30` and reparses as `17:30Z`, so 
current/future initial partitions can be classified as history and one can be 
dropped by `partition.retention_count`. Please keep this cutoff instant-bearing 
(for example `+00:00` or a directly constructed UTC `PartitionKey`) and test 
the predefined-partition, non-UTC-session path.



##########
fe/fe-core/src/test/java/org/apache/doris/catalog/DynamicPartitionTableTest.java:
##########
@@ -2219,17 +2311,147 @@ public void testTimeStampTzDynamicPartitionHourUnit() 
throws Exception {
                 Assert.assertEquals("Hour partition name length: " + 
partitionName,
                         11, partitionName.length());
 
-                // Verify range validity and UTC boundaries
+                // Verify range validity
                 Range<PartitionKey> range = item.getItems();
                 Assert.assertTrue("lower must be < upper",
                         range.lowerEndpoint().compareTo(range.upperEndpoint()) 
< 0);
 
-                // With time_zone = "+00:00", partition boundaries should be 
UTC timestamps
                 List<LiteralExpr> lowerKeys = range.lowerEndpoint().getKeys();
                 Assert.assertEquals(1, lowerKeys.size());
                 String lowerStr = lowerKeys.get(0).getStringValue();
                 Assert.assertTrue("Lower key must have +00:00 suffix: " + 
lowerStr,
                         lowerStr.contains("+00:00"));
+
+                List<LiteralExpr> upperKeys = range.upperEndpoint().getKeys();
+                Assert.assertEquals(1, upperKeys.size());
+                String upperStr = upperKeys.get(0).getStringValue();
+                Assert.assertTrue("Upper key must have +00:00 suffix: " + 
upperStr,
+                        upperStr.contains("+00:00"));
+
+                // Partition boundaries must be at whole UTC hours (0-23).
+                int lowerHour = Integer.parseInt(lowerStr.substring(11, 13));

Review Comment:
   This only proves that a parsed hour is in its valid `0..23` range; the 
adjacency checks below also pass for boundaries at `00:15, 01:15, ...`. Because 
`Asia/Shanghai` has an integral-hour offset, the previous configured-zone hour 
flooring also produces UTC `:00` boundaries, so this test does not prove the 
HOUR-specific UTC-first change. Please use a supported fractional offset such 
as `+05:45`/`Asia/Kathmandu` and assert that every lower and upper bound has 
minute and second `00`, while keeping the full-timestamp adjacency check.



##########
fe/fe-core/src/test/java/org/apache/doris/common/PropertyAnalyzerTest.java:
##########
@@ -524,4 +526,139 @@ public void testAnalyzeSequenceMap() throws 
AnalysisException {
             Assert.fail();
         }
     }
+
+    @Test
+    public void testStorageCooldownTimeWithTzOffset() throws AnalysisException 
{
+        // A known UTC instant: 2027-06-15 12:30:00 UTC
+        ZonedDateTime zdt = ZonedDateTime.of(2027, 6, 15, 12, 30, 0, 0, 
ZoneOffset.UTC);
+        long expectedMillis = zdt.toInstant().toEpochMilli();
+
+        // +00:00 suffix — must be parsed as an instant via TZ_FORMATTER
+        String cooldownStr = "2027-06-15 12:30:00+00:00";
+        Map<String, String> properties = Maps.newHashMap();
+        properties.put(PropertyAnalyzer.PROPERTIES_STORAGE_MEDIUM, "SSD");
+        properties.put(PropertyAnalyzer.PROPERTIES_STORAGE_COOLDOWN_TIME, 
cooldownStr);
+        DataProperty dp = PropertyAnalyzer.analyzeDataProperty(properties,
+                new DataProperty(TStorageMedium.SSD));
+        Assert.assertEquals("TZ offset +00:00 should parse as exact UTC 
instant",
+                expectedMillis, dp.getCooldownTimeMs());
+
+        // -05:00 offset — same instant, different representation
+        String cooldownStr2 = "2027-06-15 07:30:00-05:00";
+        Map<String, String> properties2 = Maps.newHashMap();
+        properties2.put(PropertyAnalyzer.PROPERTIES_STORAGE_MEDIUM, "SSD");
+        properties2.put(PropertyAnalyzer.PROPERTIES_STORAGE_COOLDOWN_TIME, 
cooldownStr2);
+        DataProperty dp2 = PropertyAnalyzer.analyzeDataProperty(properties2,
+                new DataProperty(TStorageMedium.SSD));
+        Assert.assertEquals("TZ offset -05:00 should parse as same UTC 
instant",
+                expectedMillis, dp2.getCooldownTimeMs());
+    }
+
+    @Test
+    public void testStorageCooldownTimeWithTzOffsetFractionalSeconds() throws 
AnalysisException {
+        // A known UTC instant with fractional seconds: 2027-06-15 
12:30:00.123456 UTC
+        ZonedDateTime zdt = ZonedDateTime.of(2027, 6, 15, 12, 30, 0, 
123456000, ZoneOffset.UTC);
+        long expectedMillis = zdt.toInstant().toEpochMilli();
+
+        // TIMESTAMPTZ(6) style string with +00:00 suffix
+        String cooldownStr = "2027-06-15 12:30:00.123456+00:00";
+        Map<String, String> properties = Maps.newHashMap();
+        properties.put(PropertyAnalyzer.PROPERTIES_STORAGE_MEDIUM, "SSD");
+        properties.put(PropertyAnalyzer.PROPERTIES_STORAGE_COOLDOWN_TIME, 
cooldownStr);
+        DataProperty dp = PropertyAnalyzer.analyzeDataProperty(properties,
+                new DataProperty(TStorageMedium.SSD));
+        Assert.assertEquals("Fractional seconds with TZ offset should parse 
correctly",
+                expectedMillis, dp.getCooldownTimeMs());
+
+        // TIMESTAMPTZ(0) — no fractional seconds, with +00:00
+        String cooldownStr2 = "2027-06-15 12:30:00+00:00";
+        Map<String, String> properties2 = Maps.newHashMap();
+        properties2.put(PropertyAnalyzer.PROPERTIES_STORAGE_MEDIUM, "SSD");
+        properties2.put(PropertyAnalyzer.PROPERTIES_STORAGE_COOLDOWN_TIME, 
cooldownStr2);
+        DataProperty dp2 = PropertyAnalyzer.analyzeDataProperty(properties2,
+                new DataProperty(TStorageMedium.SSD));
+        Assert.assertEquals("No fractional seconds (precision 0) should still 
parse",
+                ZonedDateTime.of(2027, 6, 15, 12, 30, 0, 0, ZoneOffset.UTC)
+                        .toInstant().toEpochMilli(),
+                dp2.getCooldownTimeMs());
+    }
+
+    @Test
+    public void testStorageCooldownTimeInvalidTzFormat() throws 
AnalysisException {
+        // A value that looks like it has a TZ offset but is malformed
+        // (missing minutes in offset) — should fall back to 
MAX_COOLDOWN_TIME_MS
+        // instead of throwing an exception.
+        Map<String, String> properties = Maps.newHashMap();
+        properties.put(PropertyAnalyzer.PROPERTIES_STORAGE_MEDIUM, "SSD");
+        properties.put(PropertyAnalyzer.PROPERTIES_STORAGE_COOLDOWN_TIME, 
"2027-06-15 12:30:00+00");
+        DataProperty dp = PropertyAnalyzer.analyzeDataProperty(properties,
+                new DataProperty(TStorageMedium.SSD));
+        Assert.assertEquals("Malformed TZ offset should fall back to MAX",
+                DataProperty.MAX_COOLDOWN_TIME_MS, dp.getCooldownTimeMs());
+    }
+
+    @Test
+    public void testStorageCooldownTimeStrictDateValidation() throws 
AnalysisException {
+        // An invalid calendar date with a valid TZ offset (Feb 29 in a
+        // non-leap year) must be rejected by the STRICT resolver rather
+        // than silently normalized to Feb 28, and should fall back to
+        // MAX_COOLDOWN_TIME_MS.
+        Map<String, String> properties = Maps.newHashMap();
+        properties.put(PropertyAnalyzer.PROPERTIES_STORAGE_MEDIUM, "SSD");
+        properties.put(PropertyAnalyzer.PROPERTIES_STORAGE_COOLDOWN_TIME,
+                "2027-02-29 00:00:00+00:00");
+        DataProperty dp = PropertyAnalyzer.analyzeDataProperty(properties,
+                new DataProperty(TStorageMedium.SSD));
+        Assert.assertEquals("Invalid date (Feb 29 in non-leap year) should 
fall back to MAX",
+                DataProperty.MAX_COOLDOWN_TIME_MS, dp.getCooldownTimeMs());
+
+        // Also test an impossible month (month=13)
+        Map<String, String> properties2 = Maps.newHashMap();
+        properties2.put(PropertyAnalyzer.PROPERTIES_STORAGE_MEDIUM, "SSD");
+        properties2.put(PropertyAnalyzer.PROPERTIES_STORAGE_COOLDOWN_TIME,
+                "2027-13-01 00:00:00+00:00");
+        DataProperty dp2 = PropertyAnalyzer.analyzeDataProperty(properties2,
+                new DataProperty(TStorageMedium.SSD));
+        Assert.assertEquals("Invalid month (13) should fall back to MAX",
+                DataProperty.MAX_COOLDOWN_TIME_MS, dp2.getCooldownTimeMs());
+    }
+
+    @Test
+    public void testStorageCooldownTimeDstFallbackDistinctInstants() throws 
AnalysisException {
+        // On 2026-11-01 at 2:00 AM CDT, America/Chicago falls back to
+        // 1:00 AM CST.  The wall-clock hour 01:00–02:00 occurs twice:
+        //   01:00 CDT = 06:00 UTC  (earlier)
+        //   01:00 CST = 07:00 UTC  (later)
+        // A bare DATETIME string like "2026-11-01 01:30:00" is ambiguous
+        // without an offset.  With an explicit +00:00 suffix the instant
+        // parser correctly distinguishes the two.
+
+        // Earlier occurrence: 01:30 CDT = 06:30 UTC
+        String earlierStr = "2026-11-01 06:30:00+00:00";

Review Comment:
   This fixed `2026-11-01` cooldown is passed through the public 
`analyzeDataProperty()` path, which rejects every finite SSD cooldown at or 
before `System.currentTimeMillis()`. The test therefore starts throwing after 
this date even when offset parsing is correct; the positive tests above have 
the same expiry with `2027-06-15`. Please isolate the parser from the 
future-time policy, inject/control the clock, or derive test instants that 
cannot age into the past (and derive an actual future overlap if DST semantics 
are part of the assertion).



-- 
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