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


##########
fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/AsyncMaterializationContext.java:
##########
@@ -184,12 +186,19 @@ public boolean isSuccess() {
     /**
      * Calculate partition mappings and cache
      */
-    public Map<MTMVRelatedTableIf, Map<String, Set<String>>> 
calculatePartitionMappings() throws AnalysisException {
-        if (partitionMultiFlatMap != null) {
+    public Map<MTMVRelatedTableIf, Map<String, Set<String>>> 
calculatePartitionMappings(

Review Comment:
   The cache check still performs the expensive normalization first: for an 
EXPR filter, `getEffectiveQueryUsedBaseTablePartitionMap()` copies and scans 
the MV/base partition metadata before `isPartitionMappingsCovered()` can return 
a hit. On a miss, `mtmv.calculatePartitionMappings(rawFilter)` immediately 
performs the same expansion again. Because one context can serve multiple 
`StructInfo`/relationBitset rewrites, hits retain an all-partition scan and 
misses pay it twice. Please compute the effective filter once and pass it into 
mapping generation, while keeping a cheap raw-filter/bucket coverage index so a 
hit does not normalize again; the cache test should assert expansion counts 
instead of mocking this boundary away.



##########
fe/fe-core/src/main/java/org/apache/doris/mtmv/MTMVRefreshContext.java:
##########
@@ -58,9 +59,10 @@ public Map<BaseTableInfo, MTMVSnapshotIf> 
getBaseTableSnapshotCache() {
         return baseTableSnapshotCache;
     }
 
-    public static MTMVRefreshContext buildContext(MTMV mtmv) throws 
AnalysisException {
+    public static MTMVRefreshContext buildContext(MTMV mtmv, Map<List<String>, 
Set<String>> queryUsedPartitions)
+            throws AnalysisException {
         MTMVRefreshContext context = new MTMVRefreshContext(mtmv);
-        context.partitionMappings = mtmv.calculatePartitionMappings();
+        context.partitionMappings = 
mtmv.calculatePartitionMappings(queryUsedPartitions);

Review Comment:
   The query filter stops at mapping generation: immediately afterward 
`getBaseVersions(mtmv)` copies every OLAP PCT partition and passes the complete 
list to `Partition.getVisibleVersions()`. In cloud mode that inspects the cache 
state of every partition and requests every expired entry from the meta-service 
(or all entries when the cache is disabled). `isSyncWithPartitions()` and 
refresh snapshot generation later read only names present in the completed 
mappings, so a one-bucket query over a `B`-partition PCT still performs `O(B)` 
local/cache work and can issue a `B`-entry remote request. Please derive each 
PCT table's union of mapped names and snapshot only those. Missing/null and 
empty-filter maintenance contexts remain safe because their completed mappings 
still contain every PCT name those contexts can later request; preserve the 
separate non-PCT table-version snapshots. Add a test asserting the number of 
partitions passed to visible-version collection for a small selected buck
 et.



##########
fe/fe-core/src/main/java/org/apache/doris/mtmv/MTMVPartitionExpander.java:
##########
@@ -0,0 +1,122 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package org.apache.doris.mtmv;
+
+import org.apache.doris.catalog.PartitionItem;
+import org.apache.doris.catalog.PartitionKey;
+import org.apache.doris.catalog.PartitionType;
+import org.apache.doris.catalog.RangePartitionItem;
+import org.apache.doris.common.AnalysisException;
+import org.apache.doris.datasource.mvcc.MvccSnapshot;
+import org.apache.doris.datasource.mvcc.MvccUtil;
+
+import com.google.common.collect.Maps;
+import com.google.common.collect.Range;
+import com.google.common.collect.Sets;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Optional;
+import java.util.Set;
+
+/**
+ * Utility to expand query-used partition filters to MV partition granularity
+ * using Range.encloses(), avoiding expensive dateTrunc / strToDate / 
dateIncrement
+ * per-partition operations in the rollup pipeline.
+ * Separated from MTMV to keep a lightweight dependency tree for testability —
+ * loading this class does not trigger MTMV → OlapTable → CloudReplica class 
loading.
+ */
+public class MTMVPartitionExpander {
+
+    /**
+     * Expand queryUsedPartitions to MV partition granularity for RANGE base 
tables.
+     * For example, if MV is monthly partitioned (date_trunc(month)) and base 
table is daily:
+     * - Query uses p_20250115 (Jan 15)
+     * - Find MV partition p_202501 that encloses [20250115, 20250116)
+     * - Expand to ALL daily partitions within p_202501's range [20250101, 
20250201)
+     * - Result: {p_20250101, p_20250102, ..., p_20250131}
+     */
+    public static Map<List<String>, Set<String>> 
expandToMvPartitionGranularity(
+            Map<List<String>, Set<String>> queryUsedBaseTablePartitionMap,
+            Map<String, PartitionItem> mvPartitionItems,
+            Set<MTMVRelatedTableIf> pctTables) throws AnalysisException {
+        List<Range<PartitionKey>> mvRanges = new 
ArrayList<>(mvPartitionItems.size());
+        for (PartitionItem item : mvPartitionItems.values()) {
+            mvRanges.add(((RangePartitionItem) item).getItems());
+        }
+
+        Map<List<String>, Set<String>> expanded = Maps.newHashMap();
+        for (MTMVRelatedTableIf pctTable : pctTables) {
+            List<String> qualifiers = pctTable.getFullQualifiers();
+            Set<String> queryUsedPartitions = 
queryUsedBaseTablePartitionMap.get(qualifiers);
+            if (queryUsedPartitions == null) {
+                continue;
+            }
+
+            Optional<MvccSnapshot> snapshot = 
MvccUtil.getSnapshotFromContext(pctTable);
+            if (pctTable.getPartitionType(snapshot) != PartitionType.RANGE) {
+                expanded.put(qualifiers, queryUsedPartitions);
+                continue;
+            }
+
+            Map<String, PartitionItem> basePartitionItems = 
pctTable.getAndCopyPartitionItems(snapshot);
+
+            List<Range<PartitionKey>> relevantMvRanges = new ArrayList<>();
+            for (String queriedBasePartition : queryUsedPartitions) {
+                PartitionItem baseItem = 
basePartitionItems.get(queriedBasePartition);
+                if (baseItem == null) {
+                    continue;
+                }
+                Range<PartitionKey> baseRange = ((RangePartitionItem) 
baseItem).getItems();

Review Comment:
   This expansion is quadratic for the broad internal-table scans this PR is 
intended to speed up. `QueryPartitionCollector` records every selected OLAP 
partition name, so the first nested loop does `Q * M` enclosure checks; after 
collecting `R` buckets, the second loop does another `B * R`. A query selecting 
most of an `N`-partition table can therefore do `O(N^2)` range comparisons 
before the existing generator pipeline. Please index/sort the non-overlapping 
ranges (binary lookup plus a sorted sweep is sufficient), and bypass expansion 
when the explicit selection covers the whole table. A many-partition test 
should cover both sparse and broad filters.



##########
fe/fe-core/src/main/java/org/apache/doris/mtmv/MTMVPartitionExpander.java:
##########
@@ -0,0 +1,122 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package org.apache.doris.mtmv;
+
+import org.apache.doris.catalog.PartitionItem;
+import org.apache.doris.catalog.PartitionKey;
+import org.apache.doris.catalog.PartitionType;
+import org.apache.doris.catalog.RangePartitionItem;
+import org.apache.doris.common.AnalysisException;
+import org.apache.doris.datasource.mvcc.MvccSnapshot;
+import org.apache.doris.datasource.mvcc.MvccUtil;
+
+import com.google.common.collect.Maps;
+import com.google.common.collect.Range;
+import com.google.common.collect.Sets;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Optional;
+import java.util.Set;
+
+/**
+ * Utility to expand query-used partition filters to MV partition granularity
+ * using Range.encloses(), avoiding expensive dateTrunc / strToDate / 
dateIncrement
+ * per-partition operations in the rollup pipeline.
+ * Separated from MTMV to keep a lightweight dependency tree for testability —
+ * loading this class does not trigger MTMV → OlapTable → CloudReplica class 
loading.
+ */
+public class MTMVPartitionExpander {
+
+    /**
+     * Expand queryUsedPartitions to MV partition granularity for RANGE base 
tables.
+     * For example, if MV is monthly partitioned (date_trunc(month)) and base 
table is daily:
+     * - Query uses p_20250115 (Jan 15)
+     * - Find MV partition p_202501 that encloses [20250115, 20250116)
+     * - Expand to ALL daily partitions within p_202501's range [20250101, 
20250201)
+     * - Result: {p_20250101, p_20250102, ..., p_20250131}
+     */
+    public static Map<List<String>, Set<String>> 
expandToMvPartitionGranularity(
+            Map<List<String>, Set<String>> queryUsedBaseTablePartitionMap,
+            Map<String, PartitionItem> mvPartitionItems,
+            Set<MTMVRelatedTableIf> pctTables) throws AnalysisException {
+        List<Range<PartitionKey>> mvRanges = new 
ArrayList<>(mvPartitionItems.size());
+        for (PartitionItem item : mvPartitionItems.values()) {
+            mvRanges.add(((RangePartitionItem) item).getItems());
+        }
+
+        Map<List<String>, Set<String>> expanded = Maps.newHashMap();
+        for (MTMVRelatedTableIf pctTable : pctTables) {

Review Comment:
   Expanding each PCT table independently leaves multi-PCT mappings incomplete. 
If both tables have partitions in MV buckets X and Y but the query selects A/X 
and B/Y, this loop expands A only to X and B only to Y; Transfer emits X 
without B and Y without A. `isMTMVPartitionSync()` still checks every PCT table 
and treats the missing mapping as empty, which differs from the full-refresh 
snapshot, so both otherwise current MV buckets are rejected. Build the union of 
MV buckets selected by any PCT filter first (a present null/all entry selects 
all), include every PCT table's corresponding partitions for each selected 
bucket, and key cache coverage by that same bucket union. Please add an 
end-to-end multi-PCT test with different per-table filters.



##########
fe/fe-core/src/main/java/org/apache/doris/mtmv/MTMVRelatedPartitionDescSyncLimitGenerator.java:
##########
@@ -45,7 +46,8 @@ public class MTMVRelatedPartitionDescSyncLimitGenerator 
implements MTMVRelatedPa
 
     @Override
     public void apply(MTMVPartitionInfo mvPartitionInfo, Map<String, String> 
mvProperties,
-            RelatedPartitionDescResult lastResult, List<Column> 
partitionColumns) throws AnalysisException {
+            RelatedPartitionDescResult lastResult, List<Column> 
partitionColumns,

Review Comment:
   With `partition_sync_limit` enabled, this new filter is not used until the 
following `OnePartitionCol` stage. `SyncLimit` first walks every partition of 
every PCT table and calls `isGreaterThanSpecifiedTime()`, which performs 
endpoint/date conversion (and can evaluate `str_to_date` for string-backed 
dates), only for the next generator to discard unrelated names. Intersect each 
table's items with its non-null selected-name set before the cutoff conversion; 
preserve missing/null as the established full-table path and explicit empty as 
none. A combined sync-limit plus sparse-query test should verify how many items 
are evaluated.



##########
fe/fe-core/src/main/java/org/apache/doris/catalog/MTMV.java:
##########
@@ -530,15 +553,25 @@ public Map<String, PartitionKeyDesc> 
generateMvPartitionDescs() {
      * @return mvPartitionName ==> pctTable ==> pctPartitionName
      * @throws AnalysisException
      */
-    public Map<String, Map<MTMVRelatedTableIf, Set<String>>> 
calculatePartitionMappings() throws AnalysisException {
+    public Map<String, Map<MTMVRelatedTableIf, Set<String>>> 
calculatePartitionMappings(
+            Map<List<String>, Set<String>> queryUsedBaseTablePartitionMap) 
throws AnalysisException {
         if (mvPartitionInfo.getPartitionType() == 
MTMVPartitionType.SELF_MANAGE) {
             return Maps.newHashMap();
         }
         long start = System.currentTimeMillis();
+        // For EXPR-type partitions with RANGE base tables, expand the 
query-used partition
+        // filter to MV partition granularity. This ensures complete partition 
mappings per
+        // MV partition (needed for isSyncWithPartitions correctness) while 
skipping
+        // irrelevant MV partitions entirely (the performance optimization).
+        // For nested MVs where pctTable is not in the filter, the expanded 
map is empty,
+        // so the pipeline runs without filtering (full computation) — correct 
behavior.
+        Map<String, PartitionItem> mvPartitionItems = 
getAndCopyPartitionItems();
+        Map<List<String>, Set<String>> effectiveFilter
+                = 
getEffectiveQueryUsedBaseTablePartitionMap(queryUsedBaseTablePartitionMap, 
mvPartitionItems);
         Map<String, Map<MTMVRelatedTableIf, Set<String>>> res = 
Maps.newHashMap();
         Map<PartitionKeyDesc, Map<MTMVRelatedTableIf, Set<String>>> 
pctPartitionDescs = MTMVPartitionUtil
-                .generateRelatedPartitionDescs(mvPartitionInfo, mvProperties, 
getPartitionColumns());
-        Map<String, PartitionItem> mvPartitionItems = 
getAndCopyPartitionItems();

Review Comment:
   Filtering `pctPartitionDescs` does not reduce the returned mapping 
cardinality: this loop still inserts one entry for every physical MV partition 
and allocates an empty map for every non-match, after which 
`AsyncMaterializationContext` walks all of them. A one-bucket query over an 
`M`-partition MV therefore retains `O(M)` allocation and iteration, contrary to 
the comment that irrelevant partitions are skipped entirely. In filtered 
rewrite mode, emit only physical partitions present in `pctPartitionDescs`; 
preserve the full result only for unfiltered refresh/metadata callers, and add 
an end-to-end cardinality 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