Skip to content

[fix](ivm) Stop the incremental delta from reading partitions the MV dropped - #67814

Open
yujun777 wants to merge 1 commit into
apache:masterfrom
yujun777:fix-ivm-delta-partition-scope
Open

[fix](ivm) Stop the incremental delta from reading partitions the MV dropped#67814
yujun777 wants to merge 1 commit into
apache:masterfrom
yujun777:fix-ivm-delta-partition-scope

Conversation

@yujun777

@yujun777 yujun777 commented Sep 10, 2026

Copy link
Copy Markdown
Contributor

What problem does this PR solve?

partition_sync_limit keeps only a recent slice of each base table's partitions, so an MV can be missing a partition its base table still has. The incremental delta read that base partition anyway, through the delta scan and through the join-opposite snapshot. A change to a non-partitioned dimension therefore produced delta rows for dates the MV has no partition for, and the insert failed with no partition for this tuple.

Re-syncing cannot bring an expired partition back, so the retry loop in executeIvmAttempt could never recover: the task failed after exhausting its attempts, and because the write is atomic, the partitions the MV does keep were left unrepaired as well. A strict REFRESH ... INCREMENTAL, and a scheduled refresh of an MV declared without FALLBACK, fail outright.

What changed

The incremental delta now reads only the base partitions the MV's partition definition keeps.

  • MTMVPartitionUtil.generateRelatedBasePartitionIds returns that partition set per base table partitioned by the MV's partition column, and an empty value when partition_sync_limit is not set, which is the only property that can leave the MV without a base partition.
  • IvmIncrRefreshManager passes it to IvmRewriteContext.incremental, and IvmDeltaRewriter applies it as an upper bound on the partitions each base table may be read from, intersected with ivm_partition_window_limit when that is set too.
  • The bound is the partition set the MV is aligned to rather than the partitions it already has, so a base partition whose MV partition has not been added yet stays readable: the refresh still reports the missing partition and recovers it by syncing, which is what keeps a newly added base partition working.
  • A base table absent from the set keeps its full read, since the MV partition column does not come from it and limiting it would change join results without narrowing the target MV partitions. A set covering every partition of its table leaves the plan untouched, so an MV that mirrors all of its base partitions is unaffected.
  • Only olap tables are reachable: the delta reads them through their stream and restricts a scan by partition id, which a connector table has no equivalent of.

The COMPLETE paths are unchanged.

The PR also removes the nonConcurrent group from seven mtmv_p0/ivm suites that use neither debug points nor global variables or config, so they run in the parallel pool again.

Test

  • regression-test/suites/mtmv_p0/ivm/test_ivm_partition_sync_limit.groovy (new): the MV keeps only a recent slice of the base partitions, and a late-arriving dimension row must repair the partition it keeps without failing on the one it dropped. Partitions are added by hand with literal dates and no dynamic-partition scheduler, and the expectation does not depend on the run date.
  • regression-test/suites/mtmv_p0/ivm/test_ivm_partition_sync_limit_with_window.groovy (new): partition_sync_limit and ivm_partition_window_limit together, the readable base partitions are their intersection.
  • testIncrementalScopeRestrictsDeltaToScopePartitions, testIncrementalScopeCoveringAllPartitionsLeavesScanUnchanged, testIncrementalScopeWithoutPartitionsProducesEmptyRelation
  • testHasPartitionSyncLimit, testGenerateRelatedBasePartitionIdsWithoutSyncLimit, testGenerateRelatedBasePartitionIdsOnSelfManageMv, testGenerateRelatedBasePartitionIdsWithoutMvPartitionInfo
  • test_ivm_partition_sync_limit, test_ivm_partition_sync_limit_with_window, MTMVPartitionUtilTest, IvmDeltaRewriterTest, IvmIncrRefreshManagerTest

Trace issue: #65418

…dropped

partition_sync_limit keeps only a recent slice of each base table's partitions, so the MV can be
missing a partition the base table still has. The incremental delta still read that base
partition through its stream and through the join-opposite snapshot, so a change to a
non-partitioned dimension produced delta rows for dates the MV has no partition for. The insert
failed with "no partition for this tuple", and since re-syncing cannot bring an expired partition
back, the task burned its retries and failed; the write being atomic, the partitions the MV does
keep were not repaired either.

Key changes:
- add MTMVPartitionUtil.generateRelatedBasePartitionIds, which returns the base partitions the MV's partition definition keeps, per base table partitioned by the MV's partition column
- IvmIncrRefreshManager passes that set to IvmRewriteContext.incremental, and IvmDeltaRewriter applies it as an upper bound on the partitions each base table may be read from, intersected with ivm_partition_window_limit when that is set too
- the bound is the partition set the MV is aligned to rather than the partitions it already has, so a base partition whose MV partition partition sync has yet to add stays readable and the refresh still recovers it by syncing
- a base table absent from the set keeps its full read, and a set that covers every partition of its table leaves the plan untouched, so an MV that mirrors all of its base partitions is unaffected
- add MTMVPropertyUtil.hasPartitionSyncLimit, the gate that keeps the whole restriction off MVs without the property
- remove the nonConcurrent group from seven mtmv_p0/ivm suites that use no debug points and no global variables or config, so they run in the parallel pool again

Unit Test:
- test_ivm_partition_sync_limit: new, the MV keeps only a recent slice of the base partitions and a late-arriving dimension row must repair the partition it keeps without failing on the one it dropped
- test_ivm_partition_sync_limit_with_window: new, partition_sync_limit and ivm_partition_window_limit together, the readable base partitions are their intersection
- testIncrementalScopeRestrictsDeltaToScopePartitions, testIncrementalScopeCoveringAllPartitionsLeavesScanUnchanged, testIncrementalScopeWithoutPartitionsProducesEmptyRelation
- testHasPartitionSyncLimit, testGenerateRelatedBasePartitionIdsWithoutSyncLimit, testGenerateRelatedBasePartitionIdsOnSelfManageMv, testGenerateRelatedBasePartitionIdsWithoutMvPartitionInfo
- test_ivm_partition_sync_limit, test_ivm_partition_sync_limit_with_window, MTMVPartitionUtilTest, IvmDeltaRewriterTest, IvmIncrRefreshManagerTest
@yujun777
yujun777 requested a review from morrySnow as a code owner September 10, 2026 12:15
@hello-stephen

Copy link
Copy Markdown
Contributor

Thank you for your contribution to Apache Doris.
Don't know what should be done next? See How to process your PR.

Please clearly describe your PR:

  1. What problem was fixed (it's best to include specific error reporting information). How it was fixed.
  2. Which behaviors were modified. What was the previous behavior, what is it now, why was it modified, and what possible impacts might there be.
  3. What features were added. Why was this function added?
  4. Which code was refactored and why was this part of the code refactored?
  5. Which functions were optimized and what is the difference before and after the optimization?

@yujun777

Copy link
Copy Markdown
Contributor Author

run buildall

@hello-stephen

Copy link
Copy Markdown
Contributor
TPC-H: Total hot run time: 17013 ms
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpch-tools
Tpch sf100 test result on commit dad2ef51b2143ced51af04683e1606faae5e7c4e, data reload: false

------ Round 1 ----------------------------------
============================================
q1	17609	3069	3050	3050
q2	2074	261	247	247
q3	10219	871	521	521
q4	4673	253	204	204
q5	7673	567	384	384
q6	143	114	96	96
q7	539	490	382	382
q8	9247	932	910	910
q9	3500	2411	2409	2409
q10	6521	879	714	714
q11	392	200	180	180
q12	612	262	201	201
q13	18124	1544	1171	1171
q14	157	146	141	141
q15	q16	432	398	374	374
q17	1409	899	830	830
q18	3124	2286	2259	2259
q19	1263	813	776	776
q20	371	291	213	213
q21	5615	1719	1842	1719
q22	337	263	232	232
Total cold run time: 94034 ms
Total hot run time: 17013 ms

----- Round 2, with runtime_filter_mode=off -----
============================================
q1	3430	3395	3380	3380
q2	495	388	373	373
q3	2261	2334	2221	2221
q4	1204	1176	905	905
q5	2207	2169	2139	2139
q6	174	123	89	89
q7	1063	918	887	887
q8	1586	1399	1421	1399
q9	3192	3153	3172	3153
q10	1888	1814	1645	1645
q11	362	267	250	250
q12	463	434	338	338
q13	1492	1547	1173	1173
q14	182	169	180	169
q15	q16	398	400	367	367
q17	3649	3441	3306	3306
q18	4902	4532	4791	4532
q19	1015	839	868	839
q20	1001	972	836	836
q21	3861	3245	3243	3243
q22	409	350	319	319
Total cold run time: 35234 ms
Total hot run time: 31563 ms

@hello-stephen

Copy link
Copy Markdown
Contributor
TPC-DS: Total hot run time: 83115 ms
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpcds-tools
TPC-DS sf100 test result on commit dad2ef51b2143ced51af04683e1606faae5e7c4e, data reload: false

query5	4255	416	342	342
query6	380	136	124	124
query7	4947	425	232	232
query8	291	127	122	122
query9	8696	2895	2908	2895
query10	396	225	179	179
query11	5379	1061	915	915
query12	121	72	76	72
query13	1190	449	303	303
query14	6120	2235	2144	2144
query14_1	2009	1999	1990	1990
query15	170	125	116	116
query16	927	370	323	323
query17	904	466	364	364
query18	2343	336	242	242
query19	171	141	111	111
query20	93	76	73	73
query21	207	102	87	87
query22	5533	5421	5496	5421
query23	6841	6262	6133	6133
query23_1	6249	5979	6089	5979
query24	7267	1115	794	794
query24_1	762	800	803	800
query25	429	297	258	258
query26	1227	231	129	129
query27	2788	441	256	256
query28	4648	1514	1506	1506
query29	929	447	358	358
query30	254	150	135	135
query31	823	415	330	330
query32	130	79	75	75
query33	466	213	181	181
query34	988	809	494	494
query35	400	396	355	355
query36	580	600	534	534
query37	124	82	75	75
query38	1021	858	840	840
query39	493	547	479	479
query39_1	492	451	492	451
query40	207	94	79	79
query41	60	62	60	60
query42	87	75	77	75
query43	245	252	215	215
query44	998	543	542	542
query45	122	111	103	103
query46	752	824	553	553
query47	799	782	712	712
query48	307	315	244	244
query49	580	237	208	208
query50	731	262	193	193
query51	8380	8083	8133	8083
query52	71	66	59	59
query53	190	199	150	150
query54	330	169	144	144
query55	75	57	55	55
query56	181	181	168	168
query57	675	698	670	670
query58	208	176	159	159
query59	1236	1240	1135	1135
query60	226	178	186	178
query61	125	119	115	115
query62	356	210	184	184
query63	175	142	138	138
query64	2800	684	570	570
query65	1658	1665	1671	1665
query66	1774	255	218	218
query67	10263	10057	9996	9996
query68	3022	1192	749	749
query69	359	227	203	203
query70	686	629	625	625
query71	262	167	164	164
query72	2297	1688	1505	1505
query73	662	595	355	355
query74	1998	1248	1160	1160
query75	1199	1097	966	966
query76	2388	725	519	519
query77	257	259	219	219
query78	4031	3714	3329	3329
query79	2795	839	600	600
query80	1609	323	278	278
query81	516	160	135	135
query82	594	125	99	99
query83	281	205	193	193
query84	276	113	86	86
query85	815	335	277	277
query86	472	178	171	171
query87	1028	985	904	904
query88	2963	2131	2095	2095
query89	277	194	178	178
query90	2008	119	130	119
query91	131	120	99	99
query92	93	72	69	69
query93	1957	1111	688	688
query94	622	259	209	209
query95	518	254	295	254
query96	791	570	277	277
query97	1097	1072	1035	1035
query98	178	145	136	136
query99	423	340	310	310
Total cold run time: 180748 ms
Total hot run time: 83115 ms

@hello-stephen

Copy link
Copy Markdown
Contributor
ClickBench: Total hot run time: 14.83 s
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/clickbench-tools
ClickBench test result on commit dad2ef51b2143ced51af04683e1606faae5e7c4e, data reload: false

query1	0.00	0.01	0.00
query2	0.08	0.04	0.04
query3	0.24	0.11	0.11
query4	1.60	0.09	0.10
query5	0.18	0.16	0.17
query6	1.25	0.70	0.65
query7	0.03	0.00	0.00
query8	0.04	0.03	0.03
query9	0.28	0.22	0.26
query10	0.37	0.35	0.34
query11	0.16	0.12	0.11
query12	0.15	0.13	0.12
query13	0.30	0.31	0.32
query14	0.46	0.46	0.46
query15	0.37	0.36	0.35
query16	0.20	0.23	0.21
query17	0.65	0.67	0.71
query18	0.19	0.17	0.17
query19	1.25	1.21	1.22
query20	0.01	0.02	0.02
query21	15.44	0.17	0.11
query22	5.06	0.05	0.04
query23	16.17	0.26	0.10
query24	3.18	0.33	0.28
query25	0.10	0.04	0.03
query26	0.73	0.16	0.12
query27	0.03	0.03	0.02
query28	3.63	0.54	0.27
query29	12.50	3.23	2.56
query30	0.25	0.12	0.13
query31	2.76	0.38	0.17
query32	3.51	0.33	0.24
query33	1.41	1.42	1.43
query34	15.36	2.24	1.82
query35	1.78	1.77	1.76
query36	0.45	0.30	0.29
query37	0.06	0.04	0.03
query38	0.05	0.03	0.02
query39	0.04	0.02	0.03
query40	0.12	0.08	0.07
query41	0.08	0.03	0.02
query42	0.03	0.03	0.02
query43	0.03	0.03	0.03
Total cold run time: 90.58 s
Total hot run time: 14.83 s

@yujun777

Copy link
Copy Markdown
Contributor Author

run vault_p0

@yujun777

Copy link
Copy Markdown
Contributor Author

run feut

@hello-stephen

Copy link
Copy Markdown
Contributor

FE UT Coverage Report

Increment line coverage 60.53% (46/76) 🎉
Increment coverage report
Complete coverage report

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants