Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 36 additions & 3 deletions fe/fe-core/src/main/java/org/apache/doris/catalog/MTMV.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import org.apache.doris.mtmv.MTMVCache;
import org.apache.doris.mtmv.MTMVJobInfo;
import org.apache.doris.mtmv.MTMVJobManager;
import org.apache.doris.mtmv.MTMVPartitionExpander;
import org.apache.doris.mtmv.MTMVPartitionInfo;
import org.apache.doris.mtmv.MTMVPartitionInfo.MTMVPartitionType;
import org.apache.doris.mtmv.MTMVPartitionUtil;
Expand All @@ -56,6 +57,7 @@
import org.apache.logging.log4j.Logger;

import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;
Expand Down Expand Up @@ -460,6 +462,27 @@ public Map<String, PartitionKeyDesc> generateMvPartitionDescs() {
return result;
}

/**
* Normalize query-used base table partitions to the effective filter consumed by
* partition-mapping generation.
*/
public Map<List<String>, Set<String>> getEffectiveQueryUsedBaseTablePartitionMap(
Map<List<String>, Set<String>> queryUsedBaseTablePartitionMap) throws AnalysisException {
return getEffectiveQueryUsedBaseTablePartitionMap(queryUsedBaseTablePartitionMap, null);
}

private Map<List<String>, Set<String>> getEffectiveQueryUsedBaseTablePartitionMap(
Map<List<String>, Set<String>> queryUsedBaseTablePartitionMap,
Map<String, PartitionItem> mvPartitionItems) throws AnalysisException {
if (queryUsedBaseTablePartitionMap.isEmpty()
|| mvPartitionInfo.getPartitionType() != MTMVPartitionType.EXPR) {
return queryUsedBaseTablePartitionMap;
}
return MTMVPartitionExpander.expandToMvPartitionGranularity(queryUsedBaseTablePartitionMap,
mvPartitionItems != null ? mvPartitionItems : getAndCopyPartitionItems(),
mvPartitionInfo.getPctTables());
}

/**
* Calculate the partition and associated partition mapping relationship of the MTMV
* It is the result of real-time comparison calculation, so there may be some costs,
Expand All @@ -468,15 +491,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();
.generateRelatedPartitionDescs(mvPartitionInfo, mvProperties, getPartitionColumns(),
effectiveFilter);
for (Entry<String, PartitionItem> entry : mvPartitionItems.entrySet()) {
res.put(entry.getKey(),
pctPartitionDescs.getOrDefault(entry.getValue().toPartitionKeyDesc(), Maps.newHashMap()));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,7 @@ public void run() throws JobException {
}
MetaLockUtils.readLockTables(tableIfs);
try {
context = MTMVRefreshContext.buildContext(mtmv);
context = MTMVRefreshContext.buildContext(mtmv, Maps.newHashMap());
this.needRefreshPartitions = calculateNeedRefreshPartitions(context);
} finally {
MetaLockUtils.readUnlockTables(tableIfs);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
// 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.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.NavigableMap;
import java.util.Optional;
import java.util.Set;
import java.util.TreeMap;

/**
* 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 {
NavigableMap<PartitionKey, Range<PartitionKey>> mvRanges = new TreeMap<>();
for (PartitionItem item : mvPartitionItems.values()) {
Range<PartitionKey> range = ((RangePartitionItem) item).getItems();
mvRanges.put(range.lowerEndpoint(), range);
}

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

NavigableMap<PartitionKey, Range<PartitionKey>> relevantMvRanges = new TreeMap<>();
for (String queriedBasePartition : queryUsedPartitions) {
PartitionItem baseItem = basePartitionItems.get(queriedBasePartition);
if (baseItem == null) {
continue;
}
Range<PartitionKey> baseRange = ((RangePartitionItem) baseItem).getItems();
Range<PartitionKey> mvRange = findEnclosingRange(mvRanges, baseRange);
if (mvRange != null) {
relevantMvRanges.put(mvRange.lowerEndpoint(), mvRange);
}
}

if (relevantMvRanges.isEmpty()) {
expanded.put(qualifiers, Sets.newHashSet());
continue;
}

Set<String> expandedPartitions = Sets.newHashSet();
for (Entry<String, PartitionItem> baseEntry : basePartitionItems.entrySet()) {
Range<PartitionKey> baseRange = ((RangePartitionItem) baseEntry.getValue()).getItems();
if (findEnclosingRange(relevantMvRanges, baseRange) != null) {
expandedPartitions.add(baseEntry.getKey());
}
}

expanded.put(qualifiers, expandedPartitions);
}

return expanded;
}

private static Range<PartitionKey> findEnclosingRange(
NavigableMap<PartitionKey, Range<PartitionKey>> ranges, Range<PartitionKey> baseRange) {
// RANGE partitions do not overlap, so only the range with the nearest lower endpoint can enclose baseRange.
Entry<PartitionKey, Range<PartitionKey>> candidate = ranges.floorEntry(baseRange.lowerEndpoint());
return candidate != null && candidate.getValue().encloses(baseRange) ? candidate.getValue() : null;
}

private MTMVPartitionExpander() {
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
import org.apache.logging.log4j.Logger;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
Expand Down Expand Up @@ -130,7 +131,7 @@ public static boolean isMTMVPartitionSync(MTMVRefreshContext refreshContext, Str
public static Pair<List<String>, List<PartitionKeyDesc>> alignMvPartition(MTMV mtmv) throws AnalysisException {
Map<String, PartitionKeyDesc> mtmvPartitionDescs = mtmv.generateMvPartitionDescs();
Set<PartitionKeyDesc> relatedPartitionDescs = generateRelatedPartitionDescs(mtmv.getMvPartitionInfo(),
mtmv.getMvProperties(), mtmv.getPartitionColumns()).keySet();
mtmv.getMvProperties(), mtmv.getPartitionColumns(), Maps.newHashMap()).keySet();
List<String> partitionsToDrop = new ArrayList<>();
List<PartitionKeyDesc> partitionsToAdd = new ArrayList<>();
// drop partition of mtmv
Expand Down Expand Up @@ -165,7 +166,7 @@ public static List<AllPartitionDesc> getPartitionDescsByRelatedTable(
List<AllPartitionDesc> res = Lists.newArrayList();
HashMap<String, String> partitionProperties = Maps.newHashMap();
Set<PartitionKeyDesc> relatedPartitionDescs = generateRelatedPartitionDescs(mvPartitionInfo, mvProperties,
partitionColumns)
partitionColumns, Maps.newHashMap())
.keySet();
for (PartitionKeyDesc partitionKeyDesc : relatedPartitionDescs) {
SinglePartitionDesc singlePartitionDesc = new SinglePartitionDesc(true,
Expand All @@ -178,13 +179,24 @@ public static List<AllPartitionDesc> getPartitionDescsByRelatedTable(
return res;
}

/**
* generateRelatedPartitionDescs
*
* @param mvPartitionInfo materialized view mvPartitionInfo
* @param mvProperties materialized view mvProperties when created
* @param partitionColumns materialized view partition columns
* @param queryUsedPartitions partitions current query used
* @return map of mv related table partition descs
* @throws AnalysisException
*/
public static Map<PartitionKeyDesc, Map<MTMVRelatedTableIf, Set<String>>> generateRelatedPartitionDescs(
MTMVPartitionInfo mvPartitionInfo,
Map<String, String> mvProperties, List<Column> partitionColumns) throws AnalysisException {
Map<String, String> mvProperties, List<Column> partitionColumns,
Map<List<String>, Set<String>> queryUsedPartitions) throws AnalysisException {
long start = System.currentTimeMillis();
RelatedPartitionDescResult result = new RelatedPartitionDescResult();
for (MTMVRelatedPartitionDescGeneratorService service : partitionDescGenerators) {
service.apply(mvPartitionInfo, mvProperties, result, partitionColumns);
service.apply(mvPartitionInfo, mvProperties, result, partitionColumns, queryUsedPartitions);
}
if (LOG.isDebugEnabled()) {
LOG.debug("generateRelatedPartitionDescs use [{}] mills, mvPartitionInfo is [{}]",
Expand All @@ -205,8 +217,8 @@ public static boolean isMTMVSync(MTMV mtmv) {
return false;
}
try {
return isMTMVSync(MTMVRefreshContext.buildContext(mtmv), mtmvRelation.getBaseTablesOneLevelAndFromView(),
Sets.newHashSet());
return isMTMVSync(MTMVRefreshContext.buildContext(mtmv, Maps.newHashMap()),
mtmvRelation.getBaseTablesOneLevelAndFromView(), Sets.newHashSet());
} catch (AnalysisException e) {
LOG.warn("isMTMVSync failed: ", e);
return false;
Expand Down Expand Up @@ -247,7 +259,7 @@ public static Map<Long, List<String>> getPartitionsUnSyncTables(MTMV mtmv)
throws AnalysisException {
List<Long> partitionIds = mtmv.getPartitionIds();
Map<Long, List<String>> res = Maps.newHashMap();
MTMVRefreshContext context = MTMVRefreshContext.buildContext(mtmv);
MTMVRefreshContext context = MTMVRefreshContext.buildContext(mtmv, Maps.newHashMap());
for (Long partitionId : partitionIds) {
String partitionName = mtmv.getPartitionOrAnalysisException(partitionId).getName();
res.put(partitionId, getPartitionUnSyncTables(context, partitionName));
Expand Down Expand Up @@ -630,22 +642,34 @@ public static Type getPartitionColumnType(MTMVRelatedTableIf relatedTable, Strin
throw new AnalysisException("can not getPartitionColumnType by:" + col);
}

public static MTMVBaseVersions getBaseVersions(MTMV mtmv) throws AnalysisException {
return new MTMVBaseVersions(getTableVersions(mtmv), getPartitionVersions(mtmv));
public static MTMVBaseVersions getBaseVersions(MTMV mtmv,
Map<String, Map<MTMVRelatedTableIf, Set<String>>> partitionMappings) throws AnalysisException {
return new MTMVBaseVersions(getTableVersions(mtmv), getPartitionVersions(mtmv, partitionMappings));
}

private static Map<MTMVRelatedTableIf, Map<String, Long>> getPartitionVersions(MTMV mtmv) throws AnalysisException {
private static Map<MTMVRelatedTableIf, Map<String, Long>> getPartitionVersions(MTMV mtmv,
Map<String, Map<MTMVRelatedTableIf, Set<String>>> partitionMappings) throws AnalysisException {
Map<MTMVRelatedTableIf, Map<String, Long>> res = Maps.newHashMap();
if (mtmv.getMvPartitionInfo().getPartitionType().equals(MTMVPartitionType.SELF_MANAGE)) {
return res;
}
Map<MTMVRelatedTableIf, Set<String>> mappedPartitionNames = Maps.newHashMap();
for (Map<MTMVRelatedTableIf, Set<String>> mapping : partitionMappings.values()) {
for (Entry<MTMVRelatedTableIf, Set<String>> entry : mapping.entrySet()) {
mappedPartitionNames.computeIfAbsent(entry.getKey(), key -> Sets.newHashSet())
.addAll(entry.getValue());
}
}
Set<MTMVRelatedTableIf> pctTables = mtmv.getMvPartitionInfo().getPctTables();
for (MTMVRelatedTableIf pctTable : pctTables) {
if (!(pctTable instanceof OlapTable)) {
continue;
}
Map<String, Long> onePctResult = Maps.newHashMap();
List<Partition> partitions = Lists.newArrayList(((OlapTable) pctTable).getPartitions());
List<Partition> partitions = Lists.newArrayList();
for (String partitionName : mappedPartitionNames.getOrDefault(pctTable, Collections.emptySet())) {
partitions.add(((OlapTable) pctTable).getPartitionOrAnalysisException(partitionName));
}
List<Long> versions = null;
try {
versions = Partition.getVisibleVersions(partitions);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@

import com.google.common.collect.Maps;

import java.util.List;
import java.util.Map;
import java.util.Set;

Expand Down Expand Up @@ -58,10 +59,11 @@ 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.baseVersions = MTMVPartitionUtil.getBaseVersions(mtmv);
context.partitionMappings = mtmv.calculatePartitionMappings(queryUsedPartitions);
context.baseVersions = MTMVPartitionUtil.getBaseVersions(mtmv, context.partitionMappings);
return context;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@

import java.util.List;
import java.util.Map;
import java.util.Set;

/**
* Interface for a series of processes to generate PartitionDesc
Expand All @@ -37,5 +38,6 @@ public interface MTMVRelatedPartitionDescGeneratorService {
* @throws AnalysisException
*/
void apply(MTMVPartitionInfo mvPartitionInfo, Map<String, String> mvProperties,
RelatedPartitionDescResult lastResult, List<Column> partitionColumns) throws AnalysisException;
RelatedPartitionDescResult lastResult, List<Column> partitionColumns,
Map<List<String>, Set<String>> queryUsedPartitionMap) throws AnalysisException;
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,10 @@ public class MTMVRelatedPartitionDescInitGenerator implements MTMVRelatedPartiti

@Override
public void apply(MTMVPartitionInfo mvPartitionInfo, Map<String, String> mvProperties,
RelatedPartitionDescResult lastResult, List<Column> partitionColumns) throws AnalysisException {
RelatedPartitionDescResult lastResult, List<Column> partitionColumns,
Map<List<String>, Set<String>> queryUsedPartitionMap) throws AnalysisException {
Set<MTMVRelatedTableIf> relatedTables = mvPartitionInfo.getPctTables();
// the key is related table, the value is partition items of the related table
Map<MTMVRelatedTableIf, Map<String, PartitionItem>> items = Maps.newHashMap();
for (MTMVRelatedTableIf relatedTable : relatedTables) {
items.put(relatedTable,
Expand Down
Loading
Loading