Skip to content

[fix](iceberg) Fail on missing snapshot files - #68142

Merged
Gabriel39 merged 2 commits into
apache:masterfrom
Gabriel39:fix/iceberg-missing-data-files-master
Sep 18, 2026
Merged

Gabriel39 merged 2 commits into
apache:masterfrom
Gabriel39:fix/iceberg-missing-data-files-master

Conversation

@Gabriel39

Copy link
Copy Markdown
Contributor

What problem does this PR solve?

An Iceberg snapshot can reference a data file that has been removed from storage. With the default ignore_not_found_file_in_external_table=true, both file scanners can skip the missing file and return a successful but incomplete result.

Exclude Iceberg ranges from the missing-file skip policy, regardless of that setting, while preserving the existing behavior for other table formats. Apply the shared policy at reader initialization and read boundaries. This also prevents a missing delete file reported as NOT_FOUND from being swallowed by the scanner.

Add unit coverage for Iceberg Parquet/ORC/JNI and ordinary external ranges with both setting values. Add an external regression that creates multiple data files, deletes one actual object, and expects row scans to fail with V1/V2 and both setting values. The regression disables file/result caches and restores the BE configuration after testing.

Release note

Iceberg scans now fail when a required snapshot file is missing instead of silently skipping it, including when ignore_not_found_file_in_external_table is enabled.

Check List (For Author)

  • Test: The shared-policy GoogleTests passed in a standalone build with actual generated Thrift types; the Iceberg assertion failed with the extracted original policy. Groovy compilation, clang-format 16, and whitespace checks passed. The full BE UT runner was blocked during local clucene/Zstd dependency configuration. The external regression has not been executed locally.
  • Behavior changed: Yes; missing Iceberg files are no longer ignored. Other table formats retain their existing behavior.
  • Does this need documentation: No new configuration or API; the corrected behavior is described above.

### What problem does this PR solve?

An Iceberg snapshot can reference a data file that has been removed from storage. With the default `ignore_not_found_file_in_external_table=true`, both file scanners can skip the missing file and return a successful but incomplete result.

Exclude Iceberg ranges from the missing-file skip policy, regardless of that setting, while preserving the existing behavior for other table formats. Apply the shared policy at reader initialization and read boundaries. This also prevents a missing delete file reported as `NOT_FOUND` from being swallowed by the scanner.

Add unit coverage for Iceberg Parquet/ORC/JNI and ordinary external ranges with both setting values. Add an external regression that creates multiple data files, deletes one actual object, and expects row scans to fail with V1/V2 and both setting values. The regression disables file/result caches and restores the BE configuration after testing.

### Release note

Iceberg scans now fail when a required snapshot file is missing instead of silently skipping it, including when `ignore_not_found_file_in_external_table` is enabled.

### Check List (For Author)

- Test: The shared-policy GoogleTests passed in a standalone build with actual generated Thrift types; the Iceberg assertion failed with the extracted original policy. Groovy compilation, clang-format 16, and whitespace checks passed. The full BE UT runner was blocked during local clucene/Zstd dependency configuration. The external regression has not been executed locally.
- Behavior changed: Yes; missing Iceberg files are no longer ignored. Other table formats retain their existing behavior.
- Does this need documentation: No new configuration or API; the corrected behavior is described above.
@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?

@Gabriel39

Copy link
Copy Markdown
Contributor Author

run buildall

@Gabriel39

Copy link
Copy Markdown
Contributor Author

/review

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Static review at exact head 9aba6d7b9fda43520404458f75b5665bebb9fc80.

The implementation goal is sound: missing files belonging to a committed Iceberg snapshot must fail the query instead of being silently skipped. The shared predicate is small and focused, and all three V1 plus both V2 NOT_FOUND decisions use it. However, the new end-to-end regression deterministically fails during setup, so it never proves the intended behavior; I left one blocking inline comment.

Critical checkpoints:

  • Goal and proof: the scanner change accomplishes the intended error policy, but the regression proof is currently invalid because of the URI-scheme assertion.
  • Scope and clarity: the production change is narrowly scoped and reuses one helper; no unrelated behavior was added.
  • Concurrency and lifecycle: no new shared state, locks, threads, static initialization, ownership, or allocation is introduced. Existing V1/V2 abort, close, block-clearing, and finished-range behavior remains intact.
  • Configuration: no configuration item is added. The existing mutable ignore setting is read at each failure boundary; the regression restores per-BE values.
  • Compatibility and parallel paths: no protocol or storage-format change is introduced. Current Iceberg native-data, JNI/system-table, position-delete, and count-related ranges carry the same exact per-range iceberg marker used by reader selection; local and remote split paths copy it. Data-file, delete-resource, and row-id error paths were traced across both scanners.
  • Error handling and observability: Iceberg NOT_FOUND statuses remain fatal with existing path/status context; non-Iceberg skip behavior and counters are preserved. No additional metric or log is warranted for this policy-only change.
  • Transactions, persistence, and data writes: not modified by this PR. No new FE-BE variable or persisted field is introduced.
  • Performance: the added work is a constant-time predicate only on NOT_FOUND handling; no hot-path allocation or repeated scan is added.
  • Tests: the helper unit covers Iceberg and non-Iceberg truth tables and is discoverable by the BE test glob. The external regression intends to cover both scanners and both config values, but cannot reach them until the inline issue is fixed. Per the review contract, this was a static-only review: no builds, format checks, or tests were run.

User focus: no additional focus points were provided; the complete PR was reviewed.

def files = sql "select file_path from missing_data_file\$files where content = 0"
assertTrue(files.size() >= 2, "The snapshot must contain multiple data files")
URI missingFile = new URI(files[0][0].toString())
assertEquals("s3", missingFile.scheme)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P1] Accept the fixture's s3a data-file URI

This fixture configures the REST warehouse as s3a://warehouse/wh/, and the write path deliberately commits the preserved raw output path. Consequently $files returns an s3a://... URI here, so this assertion fails before deleteObject or any V1/V2/configuration case runs. Please accept s3a (or remove the scheme assertion) while retaining the host/path checks used for MinIO deletion.

@hello-stephen

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

------ Round 1 ----------------------------------
============================================
q1	17706	3886	3985	3886
q2	2224	328	276	276
q3	10079	1392	792	792
q4	4681	469	350	350
q5	7502	817	540	540
q6	187	166	135	135
q7	745	776	582	582
q8	9305	1634	1602	1602
q9	5450	4236	4170	4170
q10	6749	1604	1348	1348
q11	424	274	252	252
q12	630	419	297	297
q13	18077	2623	1990	1990
q14	257	247	234	234
q15	q16	721	723	677	677
q17	1788	1215	1086	1086
q18	6595	5573	5511	5511
q19	1166	1214	1051	1051
q20	483	408	272	272
q21	5625	2541	2281	2281
q22	414	358	302	302
Total cold run time: 100808 ms
Total hot run time: 27634 ms

----- Round 2, with runtime_filter_mode=off -----
============================================
q1	4280	4086	4143	4086
q2	763	607	570	570
q3	4518	4901	4427	4427
q4	2228	2322	1412	1412
q5	4282	4145	4187	4145
q6	220	171	125	125
q7	1697	1626	1424	1424
q8	2206	2612	2044	2044
q9	7511	7390	7486	7390
q10	4293	4246	3926	3926
q11	567	440	408	408
q12	722	719	539	539
q13	2553	2866	2120	2120
q14	299	298	259	259
q15	q16	709	754	815	754
q17	7925	7246	7037	7037
q18	11897	11113	11769	11113
q19	1186	1072	1067	1067
q20	2222	2225	1935	1935
q21	5687	4787	4580	4580
q22	511	449	397	397
Total cold run time: 66276 ms
Total hot run time: 59758 ms

@hello-stephen

Copy link
Copy Markdown
Contributor
TPC-DS: Total hot run time: 153511 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 9aba6d7b9fda43520404458f75b5665bebb9fc80, data reload: false

query5	4316	603	450	450
query6	433	188	164	164
query7	4796	544	298	298
query8	318	172	161	161
query9	8797	3925	3926	3925
query10	464	317	253	253
query11	5881	2194	1979	1979
query12	154	99	104	99
query13	1245	580	403	403
query14	6490	4559	4242	4242
query14_1	4383	3992	4008	3992
query15	203	199	177	177
query16	1003	497	420	420
query17	911	670	532	532
query18	2439	451	334	334
query19	204	180	137	137
query20	116	102	107	102
query21	218	136	117	117
query22	13029	13050	12752	12752
query23	15495	14660	14148	14148
query23_1	14250	14120	14061	14061
query24	7495	1684	1199	1199
query24_1	1241	1254	1239	1239
query25	535	439	395	395
query26	1253	319	164	164
query27	2721	545	327	327
query28	4579	1940	1975	1940
query29	1017	577	435	435
query30	315	247	208	208
query31	877	743	637	637
query32	140	93	92	92
query33	526	294	239	239
query34	1180	1080	621	621
query35	711	759	637	637
query36	813	801	705	705
query37	143	102	88	88
query38	1812	1769	1689	1689
query39	681	682	659	659
query39_1	641	664	656	656
query40	231	129	97	97
query41	65	66	64	64
query42	97	95	90	90
query43	333	341	302	302
query44	1309	686	724	686
query45	185	176	175	175
query46	985	1152	709	709
query47	1476	1507	1375	1375
query48	393	358	300	300
query49	586	399	308	308
query50	905	332	237	237
query51	10692	10358	10103	10103
query52	115	85	75	75
query53	239	250	177	177
query54	254	198	185	185
query55	75	71	67	67
query56	229	236	211	211
query57	1502	1395	1356	1356
query58	234	201	194	194
query59	1954	2035	1786	1786
query60	279	229	232	229
query61	140	142	143	142
query62	390	316	265	265
query63	214	179	170	170
query64	2824	1013	804	804
query65	4023	3959	3951	3951
query66	1777	413	302	302
query67	20058	20155	20396	20155
query68	3406	1417	945	945
query69	419	291	262	262
query70	936	908	876	876
query71	292	232	217	217
query72	2868	2432	2270	2270
query73	797	786	451	451
query74	4644	4478	4320	4320
query75	2284	2281	1933	1933
query76	2319	1087	689	689
query77	362	398	304	304
query78	9095	9054	8433	8433
query79	1249	1187	735	735
query80	529	473	395	395
query81	451	281	242	242
query82	270	164	131	131
query83	267	270	246	246
query84	297	146	110	110
query85	893	459	383	383
query86	275	230	231	230
query87	2003	1958	1851	1851
query88	3593	2767	2716	2716
query89	311	288	243	243
query90	2112	180	172	172
query91	165	152	129	129
query92	101	89	87	87
query93	1454	1555	887	887
query94	539	349	299	299
query95	645	369	337	337
query96	1072	734	345	345
query97	2434	2464	2324	2324
query98	195	203	182	182
query99	743	713	611	611
Total cold run time: 240829 ms
Total hot run time: 153511 ms

@hello-stephen

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

query1	0.01	0.01	0.00
query2	0.09	0.05	0.05
query3	0.29	0.13	0.13
query4	1.61	0.14	0.14
query5	0.25	0.23	0.22
query6	1.15	0.95	0.94
query7	0.04	0.01	0.01
query8	0.06	0.04	0.04
query9	0.41	0.34	0.34
query10	0.54	0.60	0.63
query11	0.20	0.14	0.15
query12	0.17	0.15	0.16
query13	0.48	0.48	0.47
query14	0.95	0.94	0.94
query15	0.61	0.59	0.58
query16	0.33	0.34	0.32
query17	1.12	1.07	1.10
query18	0.23	0.20	0.21
query19	1.99	1.92	1.95
query20	0.02	0.01	0.02
query21	15.42	0.23	0.14
query22	4.81	0.05	0.05
query23	16.13	0.31	0.13
query24	2.95	0.43	0.31
query25	0.11	0.05	0.04
query26	0.76	0.21	0.15
query27	0.05	0.04	0.04
query28	3.51	0.78	0.35
query29	12.49	4.10	3.19
query30	0.30	0.16	0.15
query31	2.77	0.57	0.31
query32	3.22	0.59	0.48
query33	3.18	3.09	3.24
query34	15.73	3.94	3.29
query35	3.22	3.20	3.22
query36	0.58	0.44	0.43
query37	0.09	0.07	0.06
query38	0.05	0.05	0.03
query39	0.04	0.03	0.03
query40	0.17	0.15	0.14
query41	0.08	0.03	0.03
query42	0.03	0.04	0.02
query43	0.04	0.03	0.04
Total cold run time: 96.28 s
Total hot run time: 23.96 s

@hello-stephen

Copy link
Copy Markdown
Contributor

BE Regression && UT Coverage Report

Increment line coverage 100.00% (4/4) 🎉

Increment coverage report
Complete coverage report

Category Coverage
Function Coverage 76.27% (34394/45094)
Line Coverage 61.16% (386108/631258)
Region Coverage 57.55% (324998/564745)
Branch Coverage 58.35% (148098/253801)

### What problem does this PR solve?

The Iceberg missing-data-file regression assumes metadata paths use s3://, but Hadoop-backed catalogs can return s3a://. Accept the supported s3, s3a and s3n schemes while preserving the bucket/key used for object deletion and rejecting unrelated schemes.

### Release note

None

### Check List (For Author)

- Test: Reproduced the original s3a assertion failure using the actual URI-handling snippet. Verified s3/s3a/s3n bucket/key parsing and rejection of an unrelated scheme against both branch versions; Groovy compilation and git diff --check passed. Full external regression awaits CI.
- Behavior changed: Test fixture compatibility only.
- Does this need documentation: No.
@Gabriel39

Copy link
Copy Markdown
Contributor Author

run buildall

@Gabriel39

Copy link
Copy Markdown
Contributor Author

/review

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Static review at exact head 4dbb5dba86a7f94a73c50cf964cbe0b209f21237.

No new actionable findings.

Critical checkpoints:

  • Goal and correctness: the change makes missing files from a committed Iceberg snapshot fatal instead of silently returning partial results. All three V1 and both V2 NOT_FOUND skip decisions now use the shared range predicate, while non-Iceberg behavior remains unchanged.
  • Scope and clarity: all five changed files and the relevant FE range construction, V1/V2 reader, delete-file/deletion-vector, split scheduling, cleanup, and test-harness paths were reviewed. The helper is narrowly scoped and uses the same exact iceberg marker already required for Iceberg reader routing.
  • Concurrency and lifecycle: no new shared state, locking, ownership model, or asynchronous behavior is introduced. Fatal V2 prepare/read errors correctly avoid skipped/finished-range accounting and unwind through scanner/table-reader close; V1 cleanup remains delegate-driven. No partial block can be published before the task error is observed.
  • Configuration, compatibility, and parallel paths: no configuration, protocol, or storage-format field is added. The existing mutable ignore setting still governs non-Iceberg ranges, but cannot override Iceberg snapshot correctness. Current and legacy FE range construction carries the marker for data, JNI/system-table, and native delete/deletion-vector variants; nested delete-file errors return through the marked outer range. V1/V2 row-id reads already propagate errors directly.
  • Error handling, observability, and performance: missing Iceberg objects retain path/status context and fail the query. Existing skipped-file counters remain limited to files actually skipped. The added work is a bounded field/string check with no allocation; no material performance or observability concern was found.
  • Persistence, transactions, and data writes: not changed. There is no new persisted state, FE-BE variable, or transaction boundary.
  • Tests: the helper unit test is discoverable by the BE test source glob and covers Iceberg Parquet/ORC/JNI plus non-Iceberg truth tables. The regression removes a committed MinIO data object, verifies absence, disables relevant caches, and checks both scanner versions under both values of the ignore setting with an exact NOT_FOUND substring oracle. Existing tests cover missing position-delete/deletion-vector sources and both scanner error-propagation paths. Broader ORC/delete-file end-to-end combinations would be additional coverage, but no concrete uncovered incorrect path was found.

The prior inline report about accepting the fixture's s3a URI is fixed at this head by accepting s3, s3a, and s3n, so it was not duplicated. No additional user focus was provided.

Validation disclosure: this review was static-only; I did not build or run tests. At submission time, GitHub reports formatter, Checkstyle, license, secrets, and several aggregate checks passing, while BE UT, compile, performance, and automated-review checks remain pending.

@hello-stephen

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

------ Round 1 ----------------------------------
============================================
q1	17594	3792	3775	3775
q2	2284	339	278	278
q3	10075	1383	800	800
q4	4680	478	351	351
q5	7468	825	556	556
q6	175	169	133	133
q7	705	766	578	578
q8	9296	1467	1506	1467
q9	5409	4194	4152	4152
q10	6705	1596	1338	1338
q11	428	274	245	245
q12	629	404	306	306
q13	18047	2673	1980	1980
q14	262	249	234	234
q15	q16	727	721	668	668
q17	1761	1152	960	960
q18	6494	5596	5558	5558
q19	1220	1263	1107	1107
q20	501	396	266	266
q21	5533	2535	2346	2346
q22	419	356	281	281
Total cold run time: 100412 ms
Total hot run time: 27379 ms

----- Round 2, with runtime_filter_mode=off -----
============================================
q1	4157	4129	4084	4084
q2	752	591	562	562
q3	4514	4902	4356	4356
q4	2191	2305	1414	1414
q5	4207	4101	4063	4063
q6	220	171	124	124
q7	1666	1584	1435	1435
q8	2124	1894	2171	1894
q9	7480	7319	7316	7316
q10	4264	4290	3897	3897
q11	546	422	369	369
q12	710	744	521	521
q13	2506	2844	2189	2189
q14	292	301	269	269
q15	q16	701	711	617	617
q17	7895	7069	7175	7069
q18	11956	11055	11749	11055
q19	1151	1083	1040	1040
q20	2234	2226	1918	1918
q21	5730	4772	4667	4667
q22	503	462	391	391
Total cold run time: 65799 ms
Total hot run time: 59250 ms

@hello-stephen

Copy link
Copy Markdown
Contributor
TPC-DS: Total hot run time: 152819 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 4dbb5dba86a7f94a73c50cf964cbe0b209f21237, data reload: false

query5	4328	601	441	441
query6	437	184	163	163
query7	4814	552	282	282
query8	319	193	166	166
query9	8819	3905	3925	3905
query10	458	298	252	252
query11	5818	2175	1980	1980
query12	164	97	93	93
query13	1252	592	403	403
query14	6476	4513	4215	4215
query14_1	4000	3970	3949	3949
query15	202	194	177	177
query16	987	493	454	454
query17	897	710	535	535
query18	2438	445	327	327
query19	198	175	141	141
query20	103	103	114	103
query21	240	137	119	119
query22	13004	13021	12852	12852
query23	15482	14540	13907	13907
query23_1	14094	14017	14011	14011
query24	7492	1652	1219	1219
query24_1	1211	1231	1216	1216
query25	542	421	366	366
query26	897	335	171	171
query27	2649	564	329	329
query28	4517	1946	1963	1946
query29	1015	584	453	453
query30	314	238	203	203
query31	884	752	633	633
query32	151	104	95	95
query33	553	314	247	247
query34	1167	1090	624	624
query35	725	776	655	655
query36	779	774	724	724
query37	159	101	86	86
query38	1821	1797	1702	1702
query39	678	684	662	662
query39_1	643	626	637	626
query40	197	116	94	94
query41	68	67	62	62
query42	97	88	87	87
query43	335	334	290	290
query44	1336	677	683	677
query45	178	173	175	173
query46	1031	1199	707	707
query47	1464	1498	1380	1380
query48	391	396	272	272
query49	577	394	291	291
query50	981	331	245	245
query51	10208	10296	10124	10124
query52	85	84	73	73
query53	244	252	181	181
query54	258	201	187	187
query55	77	80	65	65
query56	220	218	203	203
query57	1540	1439	1324	1324
query58	226	205	208	205
query59	1946	2059	1791	1791
query60	280	247	227	227
query61	139	139	140	139
query62	402	327	271	271
query63	219	169	174	169
query64	2285	973	794	794
query65	4044	3948	3975	3948
query66	1734	410	311	311
query67	19899	20338	20091	20091
query68	3300	1468	852	852
query69	410	295	254	254
query70	959	883	868	868
query71	293	229	202	202
query72	2910	2459	2186	2186
query73	786	724	417	417
query74	4648	4462	4304	4304
query75	2284	2248	1922	1922
query76	2342	1087	689	689
query77	358	380	280	280
query78	9012	8999	8487	8487
query79	1338	1153	709	709
query80	851	441	344	344
query81	506	282	239	239
query82	615	163	120	120
query83	372	278	239	239
query84	313	140	116	116
query85	870	454	366	366
query86	386	235	228	228
query87	2049	1993	1856	1856
query88	3736	2761	2755	2755
query89	356	287	250	250
query90	1876	186	175	175
query91	193	156	130	130
query92	95	79	89	79
query93	1614	1469	813	813
query94	608	324	289	289
query95	663	350	338	338
query96	1082	756	352	352
query97	2427	2434	2374	2374
query98	195	191	184	184
query99	746	705	611	611
Total cold run time: 239587 ms
Total hot run time: 152819 ms

@hello-stephen

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

query1	0.01	0.01	0.01
query2	0.10	0.05	0.05
query3	0.26	0.12	0.12
query4	1.61	0.14	0.14
query5	0.26	0.22	0.22
query6	1.16	0.95	0.94
query7	0.04	0.01	0.00
query8	0.06	0.04	0.04
query9	0.40	0.34	0.34
query10	0.56	0.53	0.55
query11	0.20	0.15	0.14
query12	0.18	0.15	0.14
query13	0.48	0.47	0.48
query14	0.96	0.95	0.95
query15	0.62	0.58	0.59
query16	0.31	0.33	0.32
query17	1.08	1.12	1.08
query18	0.22	0.19	0.20
query19	2.06	1.91	1.88
query20	0.02	0.01	0.01
query21	15.44	0.22	0.14
query22	4.82	0.05	0.05
query23	16.21	0.32	0.12
query24	2.93	0.40	0.32
query25	0.13	0.04	0.05
query26	0.73	0.21	0.16
query27	0.04	0.04	0.04
query28	3.54	0.84	0.34
query29	12.57	4.01	3.18
query30	0.27	0.14	0.15
query31	2.77	0.57	0.30
query32	3.22	0.58	0.49
query33	3.11	3.13	3.32
query34	15.65	3.88	3.25
query35	3.22	3.17	3.20
query36	0.57	0.42	0.41
query37	0.09	0.06	0.06
query38	0.05	0.04	0.03
query39	0.04	0.02	0.02
query40	0.19	0.15	0.14
query41	0.08	0.03	0.02
query42	0.04	0.03	0.03
query43	0.05	0.03	0.04
Total cold run time: 96.35 s
Total hot run time: 23.76 s

@Gabriel39
Gabriel39 merged commit 25ee555 into apache:master Sep 18, 2026
35 of 37 checks passed
@hello-stephen

Copy link
Copy Markdown
Contributor

BE Regression && UT Coverage Report

Increment line coverage 100.00% (4/4) 🎉

Increment coverage report
Complete coverage report

Category Coverage
Function Coverage 76.25% (34383/45091)
Line Coverage 61.14% (385929/631260)
Region Coverage 57.52% (324840/564744)
Branch Coverage 58.33% (148047/253803)

morningman added a commit that referenced this pull request Oct 26, 2026
### What problem does this PR solve?

Issue Number: None

Related PR: #69571, #68142

Problem Summary:

**In short.** When a Paimon split's deletion vector file is missing, the
native readers report `NOT_FOUND`, and both file scanners take that for
a missing data file and skip the whole range. The query succeeds and
silently drops every surviving row of the data file. This PR reports a
missing deletion vector as an error, so the query fails instead. A
missing data file is still skipped as before.

**Background.** A Paimon split for a table with deletion vectors names a
data file and the deletion vector that marks its deleted rows. The
native readers open the deletion vector before they read a row: V1 in
`PaimonParquetReader` / `PaimonOrcReader::_init_deletion_vector`, V2 in
`TableReader::_parse_delete_predicates`, both through
`DeletionVectorReader`. `FileScanner` and `FileScannerV2` skip a range
whose reader reports `NOT_FOUND` while
`ignore_not_found_file_in_external_table` is on (the default). That
setting exists for file listings that are cached by the FE and may name
a file that has since been removed. #68142 already exempted Iceberg
ranges, whose files all belong to a committed snapshot.

**The problem, and what it cost.** A deletion vector is never a stale
listing: it belongs to the snapshot that planned its data file. It can
still be missing, for example when Paimon expires the snapshot that
wrote it while the data file stays live under a newer one, or when it is
looked up under a partition name that is never found (#69571). The data
file is still there, but the scanners skip the whole range, so its
surviving rows are dropped together with the deleted ones. The BE logs
`Skip missing file` with the path of the data file, which exists.
Paimon's own reader, the JNI reader and the Rust reader fail on the same
split instead.

**How this PR fixes it.** `DeletionVectorReader`, which opens every
deletion vector for V1 Paimon, V1 Iceberg and the V2 table reader, now
reports a missing file as `INTERNAL_ERROR` with the path of the deletion
vector, both when it opens the file and when it reads it. The scanners
only skip `NOT_FOUND`, so the query fails. A missing data file still
reports `NOT_FOUND` and is skipped as before, with or without a deletion
vector. For Iceberg, which already failed on any missing file (#68142),
only the error code of a missing deletion vector changes.

One boundary: if the data file is gone as well, the result depends on
which file the reader reaches first. FileScannerV2 parses the deletion
vector while it prepares the split, before it opens the data file, so
such a split now fails; in the local run below V1 Parquet failed too,
while V1 ORC, which opens the data file first, still skipped it.

**Results.** A local cluster with a `file://` Paimon catalog, two append
tables (Parquet and ORC, `deletion-vectors.enabled = true`), five rows
each, the first row deleted through a deletion vector the way a Spark
`DELETE` persists it:

| Files on storage | Reader | Before | After |
|---|---|---|---|
| data file and deletion vector | V1 / V2, Parquet / ORC | `2,3,4,5` |
`2,3,4,5` |
| data file only (deletion vector removed) | V1 / V2, Parquet / ORC |
empty result, query succeeds; `Skip missing file: path=<data file>` in
be.INFO | `[INTERNAL_ERROR]Deletion vector file is missing: path
.../index/index-...-0, failed to get file size ...: No such file or
directory` |
| deletion vector only (data file removed) | V1 / V2, Parquet / ORC |
empty result | empty result (unchanged) |
| neither | V2, V1 Parquet | empty result | the deletion vector error
above |
| neither | V1 ORC | empty result | empty result (unchanged) |
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants