Skip to content

[fix](paimon) Read Paimon tables on BE without reconstructing the catalog metastore - #65867

Merged
CalvinKirs merged 4 commits into
apache:masterfrom
morningman:fix-27343
Jul 31, 2026
Merged

[fix](paimon) Read Paimon tables on BE without reconstructing the catalog metastore#65867
CalvinKirs merged 4 commits into
apache:masterfrom
morningman:fix-27343

Conversation

@morningman

@morningman morningman commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

What problem does this PR solve?

Reading a Paimon table over a metastore-backed or REST catalog (HMS / DLF) fails on
BE whenever the JNI reader is used — most visibly system tables (e.g. $snapshots,
$files), which always go through JNI, and also branch / time-travel / incremental
reads. Two failures were observed, both caused by the BE rebuilding a catalog it does
not need:

# HMS catalog
[JNI_ERROR] NoClassDefFoundError: org/apache/hadoop/hive/conf/HiveConf
# DLF (REST) catalog
[JNI_ERROR] ClassNotFoundException: com.aliyun.datalake.metastore.hive2.ProxyMetaStoreClient

Root cause. A table loaded from a metastore-backed Paimon catalog carries a Paimon
CatalogLoader (e.g. HiveCatalogLoader) in its CatalogEnvironment. When FE
serializes that table to BE, SnapshotManager#latestSnapshotId resolves the latest
snapshot through the catalog's SnapshotLoader, which on BE reconstructs the catalog's
metastore client and its whole Hive / DLF-REST stack — even though the BE only reads
(via FE-resolved splits and the object store) and the snapshots already live there.
This was previously masked by java-udf's ~122MB hive-catalog-shade on BE's shared
classpath; #65733 replaced it with the slim hive-udf-shade, exposing the dependency.

Fix 1 — serialize a catalog-less table to the BE (PaimonScanNode).

  • data table: rebuild via FileStoreTableFactory with an empty CatalogEnvironment.
    A FileStoreTable is fully defined by fileIO / location / schema, and its dynamic
    options (time travel, incremental) are already merged into the schema by copy(...),
    so nothing is lost except the catalog loader.
  • system table: rebuild it over such a catalog-less data table via SystemTableLoader.

With no catalog loader, SnapshotManager#latestSnapshotId lists the snapshot directory
on the filesystem instead of calling the metastore, so the BE never reconstructs the
catalog and no longer needs any Hive / metastore classes.

Fix 2 — co-locate the Paimon FileIO plugins with paimon-scanner (packaging).
Reading the snapshot from the filesystem makes the BE materialize the object-store
FileIO lazily via FileIO.get()ServiceLoader.load(FileIOLoader.class, ...),
which eagerly instantiates every registered provider. The OSS/S3 plugins
(paimon-s3S3Loader, paimon-jindoJindoLoader) were bundled in
preload-extensions, on BE's JVM system (app) classpath, but the
org.apache.paimon.fs.FileIOLoader interface they implement ships in paimon-common,
bundled only in paimon-scanner's own JniScannerClassLoader. That loader is
parent-first, so the app classloader defines S3Loader / JindoLoader and cannot
resolve the child-only FileIOLoaderNoClassDefFoundError: FileIOLoader, which
aborts discovery. Moving the two plugins into paimon-scanner puts the whole FileIO
SPI (interface + all providers) in one classloader; the Jindo SDK stays on the app
classpath (start_be.sh adds jindofs to DORIS_CLASSPATH) and is still reachable
via parent delegation.

Regression tests to re-run:
io.trino.tests.product.paimon.TestPaimonSparkCompatibility (system-table reads) and
external_table_p2/paimon/test_paimon_dlf_rest_catalog.

Release note

Fix Paimon reads (system tables, branch / time-travel / incremental) failing on BE over
metastore-backed or REST catalogs (HMS / DLF) with NoClassDefFoundError
(HiveConf / FileIOLoader) or ClassNotFoundException: ProxyMetaStoreClient.

Check List (For Author)

  • Test

    • Regression test
    • Unit Test
    • Manual test (add detailed scripts or steps below)
    • No need to test or manual test. Explain why:
  • Behavior changed:

    • No.
    • Yes.
  • Does this need documentation?

    • No.
    • Yes.

@morningman
morningman requested a review from CalvinKirs as a code owner July 22, 2026 03:23
@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?

@morningman

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.

Automated review completed. I found no substantiated blocking issue in the current one-file
change.

Part 1.3 checkpoint conclusions:

  • Goal and proof: this restores the unrelocated HiveConf and shims classes needed when FE
    serializes an HMS-backed Paimon table and the BE Paimon JNI scanner deserializes it. Both
    legacy and Format V2 affected reads converge on that boundary. The PR reports rebuilt-artifact
    smoke coverage for new HiveConf(), the SerializableHiveConf round trip, and getUser.
    Existing HMS regression coverage exercises forced JNI, incremental, time-travel, and
    system-table modes; the named branch product test was not run here.
  • Scope and clarity: this is a focused one-file dependency repair. Wildcard transitive pruning
    keeps the addition to the two small Hive artifacts rather than restoring the former catalog
    shade.
  • Concurrency and lifecycle: no threading, locking, mutable-state ownership, or resource
    lifecycle changes. The existing process-lifetime scanner classloader behavior is unchanged.
  • Configuration and compatibility: no configuration, protocol, storage-format, persistence, or
    serialized-wrapper contract changes. The Hive versions remain within the existing 3.1 family.
  • Parallel paths: legacy and Format V2 JNI modes use the same Paimon scanner artifact; native
    and C++ reader paths do not cross this Java deserialization boundary, and other scanners are
    isolated.
  • Tests and results: no result files changed. Visible CI style, license, and dependency checks
    pass, but they did not execute the deployed Paimon fat jar. The review instructions prohibited
    local builds/tests, so validation here is static plus the PR's reported manual artifact tests.
  • Observability, transactions, writes, FE-BE variables, and performance: no logging/metric,
    transaction, data-write, or FE-BE variable behavior changes. The only identified cost is the
    modest jar/startup footprint; no query hot path changes.
  • Risk closure: the wildcard-pruned external dependency exercised by HiveConf is already
    supplied by the supported BE parent classpath, and the direct Hive jars follow the existing
    assembly, deployment, and parent-first loading path. I found no substantiated closure,
    collision, or version issue.

User focus: no additional focus was provided.

@morningman

Copy link
Copy Markdown
Contributor Author

run buildall

@hello-stephen

Copy link
Copy Markdown
Contributor

FE UT Coverage Report

Increment line coverage `` 🎉
Increment coverage report
Complete coverage report

@hello-stephen

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

------ Round 1 ----------------------------------
============================================
q1	17717	4087	4161	4087
q2	2006	332	196	196
q3	10313	1433	816	816
q4	4679	468	338	338
q5	7520	867	571	571
q6	178	173	136	136
q7	757	814	617	617
q8	9355	1558	1652	1558
q9	5630	4368	4359	4359
q10	6787	1748	1471	1471
q11	515	355	327	327
q12	751	592	456	456
q13	18093	3469	2701	2701
q14	265	255	235	235
q15	q16	781	775	708	708
q17	1030	1076	968	968
q18	6791	5637	5512	5512
q19	1175	1242	1061	1061
q20	825	680	552	552
q21	5721	2562	2484	2484
q22	427	356	292	292
Total cold run time: 101316 ms
Total hot run time: 29445 ms

----- Round 2, with runtime_filter_mode=off -----
============================================
q1	4444	4453	4421	4421
q2	287	320	217	217
q3	4502	4924	4425	4425
q4	2066	2136	1375	1375
q5	4358	4273	4283	4273
q6	232	173	130	130
q7	1744	1606	2119	1606
q8	2629	2221	2222	2221
q9	8080	8109	7683	7683
q10	4791	4625	4248	4248
q11	568	433	387	387
q12	767	773	547	547
q13	3343	3481	2937	2937
q14	300	294	270	270
q15	q16	704	764	644	644
q17	1344	1351	1446	1351
q18	7881	7328	7183	7183
q19	1160	1072	1109	1072
q20	2187	2195	1906	1906
q21	5274	4554	4410	4410
q22	501	473	405	405
Total cold run time: 57162 ms
Total hot run time: 51711 ms

@hello-stephen

Copy link
Copy Markdown
Contributor
TPC-DS: Total hot run time: 176734 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 1bbb4b41c8d6324eadad37c297ee3e9f807291bb, data reload: false

query5	4324	641	491	491
query6	470	233	204	204
query7	4876	611	337	337
query8	339	192	168	168
query9	8764	4014	3996	3996
query10	496	388	296	296
query11	5900	2333	2147	2147
query12	164	103	99	99
query13	1261	566	431	431
query14	6244	5177	4838	4838
query14_1	4174	4200	4210	4200
query15	220	208	180	180
query16	1009	497	448	448
query17	944	696	567	567
query18	2480	487	347	347
query19	210	199	153	153
query20	110	108	109	108
query21	226	156	136	136
query22	13461	13436	13371	13371
query23	17295	16486	16154	16154
query23_1	16106	16209	16192	16192
query24	7465	1753	1252	1252
query24_1	1270	1269	1261	1261
query25	558	450	405	405
query26	1336	372	223	223
query27	2612	596	389	389
query28	4521	1969	1980	1969
query29	1102	638	495	495
query30	342	261	226	226
query31	1116	1090	967	967
query32	105	62	60	60
query33	529	325	244	244
query34	1187	1125	625	625
query35	782	788	672	672
query36	1195	1203	1064	1064
query37	152	106	94	94
query38	1891	1692	1644	1644
query39	863	869	847	847
query39_1	829	816	845	816
query40	268	164	139	139
query41	69	63	62	62
query42	91	93	92	92
query43	323	320	281	281
query44	1392	756	754	754
query45	196	184	166	166
query46	1044	1150	722	722
query47	2153	2132	1995	1995
query48	399	432	296	296
query49	567	413	296	296
query50	1125	463	326	326
query51	10574	10458	10359	10359
query52	90	84	74	74
query53	258	282	200	200
query54	279	233	228	228
query55	73	69	63	63
query56	308	286	284	284
query57	1307	1284	1225	1225
query58	281	265	256	256
query59	1595	1635	1438	1438
query60	315	265	252	252
query61	142	146	143	143
query62	542	492	425	425
query63	226	200	201	200
query64	2791	1057	811	811
query65	4706	4637	4597	4597
query66	1828	506	435	435
query67	29218	29134	28878	28878
query68	3069	1577	969	969
query69	409	305	260	260
query70	1078	927	936	927
query71	370	337	316	316
query72	3028	2625	2371	2371
query73	871	766	405	405
query74	5067	4868	4668	4668
query75	2525	2486	2123	2123
query76	2316	1162	754	754
query77	334	373	274	274
query78	11877	11916	11176	11176
query79	1374	1168	733	733
query80	661	567	449	449
query81	473	331	281	281
query82	563	155	123	123
query83	410	320	295	295
query84	329	157	131	131
query85	911	611	509	509
query86	364	283	291	283
query87	1824	1819	1759	1759
query88	3677	2778	2758	2758
query89	437	379	330	330
query90	1960	196	187	187
query91	204	187	159	159
query92	61	57	55	55
query93	1525	1605	1016	1016
query94	562	332	309	309
query95	779	627	479	479
query96	1060	839	345	345
query97	2628	2618	2525	2525
query98	209	199	199	199
query99	1104	1108	975	975
Total cold run time: 261383 ms
Total hot run time: 176734 ms

@hello-stephen

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

query1	0.01	0.01	0.01
query2	0.13	0.05	0.05
query3	0.25	0.13	0.13
query4	1.61	0.14	0.14
query5	0.24	0.22	0.22
query6	1.25	1.11	1.02
query7	0.05	0.01	0.01
query8	0.06	0.03	0.04
query9	0.39	0.32	0.32
query10	0.56	0.57	0.62
query11	0.19	0.13	0.13
query12	0.19	0.14	0.14
query13	0.47	0.48	0.47
query14	1.04	1.02	1.01
query15	0.61	0.59	0.60
query16	0.33	0.34	0.32
query17	1.08	1.07	1.12
query18	0.22	0.21	0.22
query19	2.08	1.92	1.91
query20	0.02	0.01	0.01
query21	15.48	0.19	0.13
query22	5.00	0.05	0.04
query23	16.17	0.30	0.12
query24	3.00	0.40	0.32
query25	0.12	0.05	0.04
query26	0.74	0.21	0.14
query27	0.04	0.04	0.03
query28	3.55	0.94	0.54
query29	12.49	4.15	3.28
query30	0.27	0.16	0.15
query31	2.77	0.58	0.30
query32	3.21	0.59	0.49
query33	3.08	3.22	3.28
query34	15.40	4.21	3.55
query35	3.54	3.53	3.53
query36	0.57	0.46	0.44
query37	0.08	0.06	0.06
query38	0.06	0.04	0.04
query39	0.04	0.03	0.02
query40	0.20	0.17	0.15
query41	0.08	0.03	0.03
query42	0.04	0.03	0.03
query43	0.04	0.03	0.04
Total cold run time: 96.75 s
Total hot run time: 25.04 s

@morningman

Copy link
Copy Markdown
Contributor Author

run buildall

@hello-stephen

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

------ Round 1 ----------------------------------
============================================
q1	17622	4079	4000	4000
q2	2038	329	212	212
q3	10239	1395	855	855
q4	4685	469	338	338
q5	7509	863	573	573
q6	177	169	137	137
q7	756	880	611	611
q8	9375	1622	1615	1615
q9	6269	4345	4336	4336
q10	6748	1737	1510	1510
q11	509	355	346	346
q12	723	571	456	456
q13	18099	3288	2728	2728
q14	284	259	250	250
q15	q16	777	774	718	718
q17	964	927	973	927
q18	6875	5653	5651	5651
q19	1326	1278	1134	1134
q20	851	679	590	590
q21	5982	2629	2426	2426
q22	438	359	301	301
Total cold run time: 102246 ms
Total hot run time: 29714 ms

----- Round 2, with runtime_filter_mode=off -----
============================================
q1	4440	4292	4261	4261
q2	294	315	215	215
q3	4560	4984	4371	4371
q4	2070	2139	1361	1361
q5	4401	4252	4264	4252
q6	231	178	127	127
q7	1749	1946	1898	1898
q8	2790	2226	2206	2206
q9	7994	8151	7834	7834
q10	4706	4655	4230	4230
q11	583	426	423	423
q12	737	796	537	537
q13	3347	3626	2981	2981
q14	306	328	291	291
q15	q16	700	722	652	652
q17	1350	1317	1459	1317
q18	7958	7409	7358	7358
q19	1136	1060	1086	1060
q20	2198	2234	1934	1934
q21	5193	4603	4385	4385
q22	504	469	397	397
Total cold run time: 57247 ms
Total hot run time: 52090 ms

@hello-stephen

Copy link
Copy Markdown
Contributor
TPC-DS: Total hot run time: 176867 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 410ed84f64126fd337a5320b373f91b2594a382e, data reload: false

query5	4327	623	488	488
query6	465	225	200	200
query7	4880	620	353	353
query8	337	189	167	167
query9	8780	4036	4049	4036
query10	457	353	295	295
query11	5901	2291	2128	2128
query12	163	102	104	102
query13	1271	617	432	432
query14	6226	5195	4882	4882
query14_1	4194	4231	4199	4199
query15	211	205	174	174
query16	1003	516	466	466
query17	935	726	576	576
query18	2441	499	351	351
query19	216	190	152	152
query20	113	108	109	108
query21	235	160	140	140
query22	13530	13718	13399	13399
query23	17365	16488	16165	16165
query23_1	16293	16178	16207	16178
query24	7585	1764	1249	1249
query24_1	1324	1293	1276	1276
query25	571	475	387	387
query26	1367	385	216	216
query27	2597	610	386	386
query28	4492	1980	1987	1980
query29	1093	616	496	496
query30	327	264	230	230
query31	1114	1090	989	989
query32	113	64	60	60
query33	550	327	260	260
query34	1155	1136	633	633
query35	775	820	676	676
query36	1202	1211	1037	1037
query37	170	105	90	90
query38	1895	1702	1645	1645
query39	897	873	850	850
query39_1	826	834	837	834
query40	243	166	145	145
query41	69	68	61	61
query42	93	93	92	92
query43	323	318	285	285
query44	1410	765	765	765
query45	208	185	172	172
query46	1031	1221	723	723
query47	2137	2088	1971	1971
query48	397	413	294	294
query49	572	412	299	299
query50	1112	427	327	327
query51	10496	10435	10586	10435
query52	87	87	76	76
query53	263	284	200	200
query54	290	239	214	214
query55	74	74	65	65
query56	300	281	287	281
query57	1315	1275	1212	1212
query58	278	261	261	261
query59	1571	1666	1430	1430
query60	299	264	245	245
query61	152	151	144	144
query62	539	494	433	433
query63	237	203	197	197
query64	2782	1046	853	853
query65	4744	4642	4618	4618
query66	1812	523	391	391
query67	29276	29277	28505	28505
query68	3107	1601	1004	1004
query69	412	309	266	266
query70	1050	975	950	950
query71	356	320	303	303
query72	3068	2686	2356	2356
query73	809	766	398	398
query74	5051	4916	4759	4759
query75	2573	2497	2134	2134
query76	2319	1184	778	778
query77	349	380	281	281
query78	11831	11765	11273	11273
query79	1402	1116	748	748
query80	655	549	457	457
query81	464	337	293	293
query82	593	149	120	120
query83	410	322	294	294
query84	335	158	129	129
query85	938	620	528	528
query86	353	279	270	270
query87	1825	1828	1733	1733
query88	3694	2743	2732	2732
query89	433	375	326	326
query90	2024	195	199	195
query91	196	189	160	160
query92	65	60	54	54
query93	1475	1518	980	980
query94	562	357	318	318
query95	801	582	473	473
query96	1089	749	350	350
query97	2607	2626	2561	2561
query98	213	208	226	208
query99	1094	1113	962	962
Total cold run time: 262015 ms
Total hot run time: 176867 ms

@hello-stephen

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

query1	0.00	0.00	0.00
query2	0.09	0.05	0.05
query3	0.26	0.14	0.14
query4	1.61	0.13	0.14
query5	0.24	0.24	0.22
query6	1.26	1.06	1.09
query7	0.04	0.02	0.00
query8	0.06	0.04	0.03
query9	0.37	0.31	0.32
query10	0.56	0.56	0.57
query11	0.19	0.14	0.14
query12	0.17	0.14	0.14
query13	0.47	0.48	0.48
query14	1.03	1.01	1.00
query15	0.62	0.63	0.60
query16	0.32	0.32	0.30
query17	1.16	1.17	1.12
query18	0.22	0.21	0.21
query19	2.10	1.99	1.86
query20	0.02	0.01	0.01
query21	15.44	0.18	0.14
query22	4.98	0.06	0.05
query23	16.13	0.31	0.12
query24	3.03	0.43	0.29
query25	0.11	0.05	0.04
query26	0.73	0.21	0.14
query27	0.05	0.04	0.03
query28	3.53	0.88	0.53
query29	12.51	4.08	3.25
query30	0.27	0.16	0.17
query31	2.76	0.58	0.31
query32	3.22	0.60	0.49
query33	3.17	3.18	3.30
query34	15.62	4.24	3.53
query35	3.48	3.49	3.53
query36	0.54	0.44	0.43
query37	0.09	0.06	0.06
query38	0.05	0.04	0.04
query39	0.03	0.02	0.02
query40	0.18	0.16	0.15
query41	0.08	0.03	0.02
query42	0.03	0.02	0.02
query43	0.04	0.04	0.03
Total cold run time: 96.86 s
Total hot run time: 24.88 s

@morningman morningman changed the title [fix](paimon) Bundle HiveConf into paimon-scanner for Paimon Hive JNI reads [fix](paimon) Read Paimon tables on BE without reconstructing the catalog metastore Jul 24, 2026
@morningman

Copy link
Copy Markdown
Contributor Author

run buildall

@morningman

Copy link
Copy Markdown
Contributor Author

/review

@hello-stephen

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

------ Round 1 ----------------------------------
============================================
q1	17672	3585	3591	3585
q2	1995	293	193	193
q3	10340	1203	754	754
q4	4679	311	236	236
q5	7606	737	517	517
q6	167	148	117	117
q7	728	705	496	496
q8	9302	1583	1605	1583
q9	4182	3102	3083	3083
q10	6788	1267	1009	1009
q11	504	324	299	299
q12	738	515	415	415
q13	18000	2584	1882	1882
q14	201	180	171	171
q15	q16	562	510	454	454
q17	1006	1067	957	957
q18	4635	3722	3381	3381
q19	1364	1208	1169	1169
q20	767	651	522	522
q21	5976	2458	2245	2245
q22	418	353	303	303
Total cold run time: 97630 ms
Total hot run time: 23371 ms

----- Round 2, with runtime_filter_mode=off -----
============================================
q1	3925	3827	3835	3827
q2	254	266	193	193
q3	2713	2979	2653	2653
q4	1382	1342	1041	1041
q5	2680	2558	2508	2508
q6	226	155	101	101
q7	1340	1171	1068	1068
q8	2008	1734	1712	1712
q9	3721	3738	3736	3736
q10	2460	2449	2178	2178
q11	425	342	315	315
q12	573	544	406	406
q13	2272	2636	2031	2031
q14	235	229	216	216
q15	q16	530	524	458	458
q17	1264	1250	1225	1225
q18	5224	4700	4680	4680
q19	1183	1179	1186	1179
q20	1114	1093	938	938
q21	4330	3729	3766	3729
q22	527	455	398	398
Total cold run time: 38386 ms
Total hot run time: 34592 ms

@github-actions

Copy link
Copy Markdown
Contributor

Codex automated review failed and did not complete.

Error: You've hit your usage limit. Visit https://chatgpt.com/codex/settings/usage to purchase more credits or try again at Jul 30th, 2026 7:58 AM.
Workflow run: https://github.com/apache/doris/actions/runs/30086680700

Please inspect the workflow logs and rerun the review after the underlying issue is resolved.

@hello-stephen

Copy link
Copy Markdown
Contributor
TPC-DS: Total hot run time: 118007 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 1f019cd6108f99dedd62ffdcb05b4b4eb7e21818, data reload: false

query5	4323	553	448	448
query6	466	219	195	195
query7	4858	594	327	327
query8	332	174	165	165
query9	8783	3895	3869	3869
query10	495	332	272	272
query11	5515	1357	1203	1203
query12	165	98	92	92
query13	1287	616	442	442
query14	6122	2877	2690	2690
query14_1	2397	2371	2394	2371
query15	199	159	142	142
query16	1093	547	495	495
query17	909	604	492	492
query18	2425	480	322	322
query19	218	199	165	165
query20	98	98	98	98
query21	233	157	128	128
query22	7646	7501	7506	7501
query23	9628	9544	9148	9148
query23_1	9437	9096	9179	9096
query24	7559	1433	1012	1012
query24_1	1035	999	1018	999
query25	538	418	379	379
query26	1296	374	219	219
query27	2569	595	391	391
query28	4441	2134	2136	2134
query29	1068	728	555	555
query30	332	265	200	200
query31	920	667	599	599
query32	127	66	67	66
query33	591	283	238	238
query34	1146	1138	663	663
query35	532	549	447	447
query36	1117	1110	1011	1011
query37	154	99	87	87
query38	1259	1034	989	989
query39	642	634	681	634
query39_1	580	587	585	585
query40	245	160	145	145
query41	70	61	68	61
query42	95	95	91	91
query43	292	291	265	265
query44	1342	773	776	773
query45	138	131	124	124
query46	1013	1087	681	681
query47	1468	1400	1353	1353
query48	393	425	342	342
query49	587	316	240	240
query50	1083	428	336	336
query51	11071	11867	11369	11369
query52	84	84	74	74
query53	253	280	206	206
query54	297	233	206	206
query55	76	71	66	66
query56	292	293	281	281
query57	920	860	859	859
query58	290	246	242	242
query59	1288	1356	1236	1236
query60	300	258	244	244
query61	149	149	142	142
query62	413	293	267	267
query63	244	195	194	194
query64	2784	858	722	722
query65	2130	2049	2042	2042
query66	1885	398	320	320
query67	16499	16559	16150	16150
query68	3404	1364	1012	1012
query69	428	308	253	253
query70	1135	1061	1019	1019
query71	372	346	306	306
query72	2914	2502	1853	1853
query73	839	738	402	402
query74	1724	1550	1456	1456
query75	1604	1519	1282	1282
query76	2371	1022	718	718
query77	325	316	274	274
query78	6200	5806	5334	5334
query79	1332	1106	721	721
query80	1273	506	463	463
query81	504	248	221	221
query82	812	142	110	110
query83	371	339	304	304
query84	330	160	129	129
query85	955	583	502	502
query86	437	344	300	300
query87	1163	1106	1040	1040
query88	3779	2972	2891	2891
query89	404	310	274	274
query90	1909	187	183	183
query91	195	188	171	171
query92	72	68	68	68
query93	1709	1475	967	967
query94	785	364	349	349
query95	759	560	448	448
query96	958	754	347	347
query97	1380	1346	1269	1269
query98	191	174	172	172
query99	608	571	511	511
Total cold run time: 209118 ms
Total hot run time: 118007 ms

@hello-stephen

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

query1	0.00	0.00	0.00
query2	0.09	0.04	0.04
query3	0.24	0.12	0.12
query4	1.61	0.13	0.12
query5	0.18	0.18	0.18
query6	1.17	0.97	0.97
query7	0.05	0.01	0.00
query8	0.05	0.04	0.03
query9	0.32	0.26	0.27
query10	0.45	0.42	0.44
query11	0.18	0.12	0.11
query12	0.17	0.12	0.13
query13	0.36	0.38	0.38
query14	0.60	0.63	0.62
query15	0.48	0.46	0.47
query16	0.26	0.28	0.24
query17	0.97	0.92	0.96
query18	0.24	0.22	0.23
query19	1.53	1.52	1.46
query20	0.02	0.01	0.02
query21	15.44	0.24	0.14
query22	4.63	0.05	0.05
query23	16.12	0.31	0.13
query24	3.01	0.44	0.30
query25	0.11	0.06	0.04
query26	0.82	0.21	0.17
query27	0.06	0.03	0.04
query28	3.40	0.87	0.44
query29	12.49	4.42	3.42
query30	0.26	0.13	0.15
query31	2.76	0.50	0.22
query32	3.32	0.42	0.28
query33	1.60	1.74	1.64
query34	15.40	2.74	2.12
query35	2.17	2.12	2.13
query36	0.49	0.33	0.35
query37	0.09	0.06	0.05
query38	0.05	0.03	0.04
query39	0.03	0.03	0.03
query40	0.15	0.12	0.11
query41	0.10	0.03	0.03
query42	0.05	0.03	0.03
query43	0.05	0.03	0.03
Total cold run time: 91.57 s
Total hot run time: 18.55 s

@morningman

Copy link
Copy Markdown
Contributor Author

/review

yiguolei
yiguolei previously approved these changes Jul 24, 2026
@github-actions github-actions Bot added the approved Indicates a PR has been approved by one committer. label Jul 24, 2026
@github-actions

Copy link
Copy Markdown
Contributor

PR approved by at least one committer and no changes requested.

@github-actions

Copy link
Copy Markdown
Contributor

PR approved by anyone and no changes requested.

@github-actions

Copy link
Copy Markdown
Contributor

Codex automated review failed and did not complete.

Error: You've hit your usage limit. Visit https://chatgpt.com/codex/settings/usage to purchase more credits or try again at Jul 30th, 2026 7:58 AM.
Workflow run: https://github.com/apache/doris/actions/runs/30099212349

Please inspect the workflow logs and rerun the review after the underlying issue is resolved.

@morningman

Copy link
Copy Markdown
Contributor Author

/review

@github-actions

Copy link
Copy Markdown
Contributor

Codex automated review failed and did not complete.

Error: You've hit your usage limit. Visit https://chatgpt.com/codex/settings/usage to purchase more credits or try again at Jul 30th, 2026 7:58 AM.
Workflow run: https://github.com/apache/doris/actions/runs/30106073957

Please inspect the workflow logs and rerun the review after the underlying issue is resolved.

@morningman

Copy link
Copy Markdown
Contributor Author

/review

@morningman

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.

Request changes. Three distinct P1 issues remain on head dcc69322483f55d3bb385f2750f3df8db8e86cf0:

  1. Final INCR option reapplication deletes the catalog-visible snapshot pin for version-managed $partitions, allowing BE to widen to filesystem-latest state.
  2. $files transfers authorization only for a fallback table's main branch before rebuilding the fallback branch with an empty catalog environment.
  3. System-table startup OPTIONS can be resolved on cache generation B and then applied and serialized on the wrapper/base pair from generation A.

Part 1.3 checkpoint conclusions:

  • Goal and proof: The patch coherently removes catalog/metastore loaders from ordinary BE table reconstruction and co-locates the Paimon FileIO SPI with its providers, but the three inline paths do not preserve the original snapshot, authorization, or generation semantics. Each was traced through Doris FE binding/serialization and the Paimon 1.3.1 BE-side reader path.
  • Scope and clarity: All eight changed files and their related runtime paths were reviewed. The dependency move and archive guards are focused; loader removal necessarily crosses catalog snapshot, fallback, authorization, and cache-lifecycle boundaries.
  • Concurrency: Volatile publication of sysBaseTable before paimonSysTable is ordered correctly, and no lock-order or deadlock issue survived. The concrete concurrency defect is the second refreshable cache lookup described inline.
  • Lifecycle: Per-plan system wrappers, FE split planning, Java table serialization, BE deserialization/reader creation, fallback dispatch, catalog publication/rollback, and process-lifetime extension classloaders were traced. No resource leak or reset defect was found beyond the reported state mismatches.
  • Configuration: No new Doris setting is introduced. Existing version-management, query.auth-enabled, scan.fallback-branch, relation OPTIONS/INCR, and optional native/JNI reader settings must retain their configured behavior; the first two findings show cases where they do not.
  • Compatibility: No storage format, persisted record, public symbol, Thrift field, serialized class version, or Paimon version changes. An old BE simply lacks this classloader fix until upgraded; no separate rolling-upgrade defect was substantiated.
  • Parallel and special paths: Ordinary tables, all 17 Paimon system-table kinds, normal/OPTIONS/INCR relations, fixed versus base-shaped schemas, native/JNI paths, direct/decorated fallback tables, empty/latest/version-managed snapshots, extension ignore modes, and S3/Jindo providers were checked. Existing $snapshots/$buckets, $files marker, fallback-bound, and packaging-test threads were not duplicated.
  • Conditions and errors: Null system-table loading, null catalog-visible latest state, explicit selector precedence, and unsupported option combinations were checked. The accepted failures can return too-new, unauthorized, or cross-generation metadata without an error, so ordinary exception handling does not contain them.
  • Tests and results: The changed FE tests cover ordinary reconstruction, pinning, one-environment authorization, option reapplication, and fallback pairing, but not INCR + partitions + version management, main-allowed/fallback-denied dispatch, or an A-then-B cache barrier. No builds or tests were run because the supplied review contract prohibits them; assembled extension artifacts are absent, and the visible Build Extensions workflow only ran change detection. No result file changed.
  • Observability: Existing scanner errors and metrics are adequate for explicit failures, but they cannot detect successful semantic widening or missed authorization. No separate logging or metric issue was found.
  • Transactions, persistence, and writes: Not applicable. This patch changes read planning, serialization, authorization transfer, and packaging; it adds no Doris transaction, EditLog, failover, or data-write path.
  • FE/BE state transfer: No new protocol variable exists; relation options and the rebuilt Paimon table travel through the existing serialized_table payload. That is why the deleted snapshot pin and frozen cross-generation selector persist to BE.
  • Performance: Reconstruction and authorization are per scan node, not per row. One additional fallback authorization call is correctness-required; no independent material CPU, memory, I/O, or hot-loop regression survived.
  • User focus: No additional review focus was supplied, so the complete PR was reviewed without a narrower theme.

The multi-agent review converged in Round 2: both normal reviewers and the risk-focused reviewer returned NO_NEW_VALUABLE_FINDINGS for this exact three-comment set, wording, positions, live duplicate state, and head SHA. The final changed-file and unresolved-candidate sweep found no unclassified point.

return rebuiltSysTable;
}
if (theScanParams.incrementalRead()) {
return rebuiltSysTable.copy(getIncrReadParams());

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] Keep incremental $partitions bounded by the catalog-visible snapshot

For a version-managed catalog, this method has just pinned the catalog-visible snapshot N, but getIncrReadParams() goes through isolateIncrementalRead(), which supplies scan.snapshot-id=null; Paimon's copy removes that pin. The supported start-only timestamp form also becomes incremental-between-timestamp=start,Long.MAX_VALUE. On BE, PartitionsRead calls newScan().listPartitionEntries(), so its incremental scanner now resolves the upper endpoint through the catalog-less SnapshotManager and can expose filesystem N+1 while REST/DLF still publishes N (or after rollback). Please resolve/cap the incremental endpoint while the catalog loader is present, or reject this composition if it cannot be represented safely, and add a catalog-N/filesystem-N+1 phase-barrier test.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Confirmed and fixed in e692e97.

Verified the chain against paimon release-1.3.1, not taken on trust:

  • The pin really is dropped: PaimonScanParams#isolateIncrementalRead nulls every inherited
    read-state key, scan.snapshot-id included, and copy(...) removes a key that maps to null
    (AbstractFileStoreTable#copyInternal:328-335). But it could not have bounded this scan either
    way: AbstractDataTableScan#createStartingScanner (:204-286) switches on options.startupMode(),
    and INCREMENTAL routes to createIncrementalStartingScanner, which never reads
    options.scanSnapshotId().
  • The timestamp form is the exposed one. IncrementalDeltaStartingScanner#betweenTimestamps
    (:168-185) turns both endpoints into snapshot ids through
    SnapshotManager#earlierOrEqualTimeMills, whose binary search runs up to latestSnapshotId()
    (SnapshotManager:288), and falls back to latestSnapshot().id() when the end resolves to
    nothing; AbstractDataTableScan:407-425 reads latestSnapshot() again for its empty-range guard.
    All three go through snapshotLoader when it exists (SnapshotManager:168-192) - the catalog
    pointer - and list the snapshot directory once it does not.
  • Scope is exactly $partitions: INCREMENTAL_SYSTEM_TABLES intersected with
    resolvesSnapshotOnBackend is {partitions}. $audit_log / $binlog / $ro / $row_tracking
    are planned on the FE and the BE only materializes their DataSplits, and the snapshot-id form
    (startSnapshotId / endSnapshotId) names its endpoints outright.

Fix: bindIncrementalRangeToCatalog resolves the range here, while the loader is still around, and
hands the BE the explicit incremental-between=startId,endId that betweenTimestamps would have
computed from the catalog's own view. Two things keep that faithful rather than approximate:

  • Only the delta scanner's start rule is reachable - incremental-between-scan-mode is cleared with
    the rest of the read state and its default AUTO never selects the diff scanner
    (AbstractDataTableScan:427-436, toSnapshotScanMode).
  • The rewrite only fires when the requested end reaches the catalog's snapshot. An older end already
    resolves to the same id on both sides, because every snapshot the catalog has not published yet is
    younger than the one it points at - which also keeps the endTimestamp < earliest.timeMillis()
    empty-range corner on Paimon's own path.

Tests: testIncrementalRangeIsResolvedOnTheCatalogVisibleSnapshot - catalog snapshot 42 at t=5000
with 43 already on the filesystem resolves to 20,42, plus the past-end and non-version-managed
no-ops - and testIncrementalPartitionsScanBindsItsRangeBeforeTheBackend, the catalog-N /
filesystem-N+1 phase barrier through getPaimonTableForBackend. The second one fails on the
previous code: earlierOrEqualTimeMills is never called, because nothing resolved the endpoint
before the BE.

FallbackReadFileStoreTable fallbackReadTable = (FallbackReadFileStoreTable) undecorated;
return new FallbackReadFileStoreTable(
rebuildWithoutCatalogLoader(fallbackReadTable.wrapped()),
rebuildWithoutCatalogLoader(fallbackReadTable.fallback()));

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] Authorize this fallback branch before erasing its catalog loader

authorizeDeferredScan() above invokes only dataTable.catalogEnvironment(), which a FallbackReadFileStoreTable delegates to its main branch. Paimon constructs this fallback branch with a separate branch-qualified identifier, and normal FallbackReadScan.plan() authorizes main and fallback plans independently. Rebuilding this branch with CatalogEnvironment.empty() makes the missed fallback check a permanent allow: a user allowed on main but denied on fallback can still read fallback $files metadata. This is reachable for a shared partition when a bucket/level predicate eliminates the main split and the fallback plan supplies it. Please authorize both captured branches with their own environments before dropping the loaders, and cover a main-allowed/fallback-denied dispatch.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Confirmed and fixed in e692e97.

Verified against paimon release-1.3.1:

  • The fallback branch does get its own environment: FileStoreTableFactory#create (:107-123)
    builds it with catalogEnvironment.copy(new Identifier(db, table, fallbackBranch)), i.e. a
    branch-qualified identifier.
  • Paimon does authorize the two branches independently: FallbackReadFileStoreTable#newScan
    (:184-187) is new FallbackReadScan(wrapped.newScan(), fallback.newScan()), each
    AbstractFileStoreTable#newScan (:268-275) passes its own
    catalogEnvironment.tableQueryAuth(coreOptions()) into DataTableBatchScan, and plan() calls
    authQuery() (DataTableBatchScan:93).
  • And the check really was lost: DelegatedFileStoreTable#catalogEnvironment (:168-169) returns
    wrapped.catalogEnvironment(), so the single call only ever reached the main branch, while
    CatalogEnvironment.empty()#tableQueryAuth returns select -> emptyList()
    (CatalogEnvironment:145-148) - a permanent allow on the BE.

Fix: authorizeDeferredScan peels decorators and authorizes each captured branch with its own
environment and its own coreOptions(), since query-auth.enabled is per-branch too. Test
testFallbackBranchIsAuthorizedBeforeDroppingTheCatalogLoader asserts Catalog#authTableQuery is
called once per identifier - db.tbl and db.tbl$branch_fb. The fallback expectation fails on the
previous code.

);
sourceTable.makeSureInitialized();
// System tables ignore snapshot semantics, so the empty snapshot resolves the base table.
Table baseTable = sourceTable.getPaimonTable(Optional.empty());

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] Resolve system-table OPTIONS on the base generation captured here

This publishes the system wrapper and sysBaseTable from generation A, but getSysPaimonTable(TableScanParams) later resolves startup OPTIONS through another sourceTable.getBasePaimonTable() lookup. Refresh-after-write or explicit invalidation can make that lookup return generation B; TableScanParams then freezes B's snapshot/tag and applies it to the wrapper over A, which is also what PaimonScanNode serializes. Across a drop/recreate or location change this can fail on a selector missing from A, or read the old incarnation when snapshot IDs collide. Please resolve OPTIONS against the captured base associated with this wrapper and add an A-then-B cache phase-barrier test.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed in e692e97, with one correction to the framing.

The split lookup is not something this PR introduced. getSysPaimonTable(TableScanParams) and its
sourceTable.getBasePaimonTable() resolution are untouched upstream code, and before this PR the
wrapper came from an independent catalog.getPaimonTable(nameMapping, "main", sysTableType) load
rather than from the meta cache - so the wrapper and the resolution base were already two different
sources, diverging across a strictly wider window. What this PR changed is that both now come from
the same cache, at two different times.

Worth closing anyway, and it is this PR's own thesis, so: the resolution now runs against the base
getSysPaimonTable() captured (getOptionsResolutionTable()). Only the catalog fallback - format /
object tables, which have no captured base - still looks the table up. The captured base is the
undecorated one, but every call resolveOptions makes on it (options(), snapshotManager(),
changelogManager(), coreOptions(), latestSnapshot(), copy()) delegates through Paimon's
decorators unchanged, so the resolved selector is identical.

Test testSystemTableOptionsResolveOnTheCapturedBaseGeneration: the captured generation is at
snapshot 1, the refreshed cache at snapshot 2, and @options('scan.mode'='latest') has to freeze
scan.snapshot-id=1. It resolves to 2 on the previous code.

@morningman

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.

Request changes: the catalog-less BE table reconstruction still loses branch-local catalog state in two fallback paths.

Blocking findings

  • The incremental $partitions@incr timestamp range is decided and resolved through the main branch only. The fallback branch can either read beyond its catalog-visible pointer or interpret the main branch's explicit snapshot IDs as its own history.
  • Reapplying resolved relation OPTIONS after both loaders are removed discards the fallback selector already resolved on FE and can make FE and BE use different fallback snapshots.

Both are P1 correctness issues, with branch-skew regression-test requirements in the inline comments.

Checkpoint conclusions

  • Goal and scope: Moving the Paimon FileIO providers into the scanner artifact and removing the BE catalog loader directly address the reported classloading and metastore-reconstruction failures. All eight changed files are relevant and the implementation is localized, but fallback behavior prevents the end-to-end objective from being correct in every advertised branch, incremental, and OPTIONS mode.
  • Correctness and lifecycle: The captured system/base table publication ordering, decorator peeling, five deferred-reader classifications, and branch-by-branch deferred authorization transfer are coherent. The two inline fallback defects remain blocking. Existing live threads on catalog-visible-empty / $snapshots / $buckets, implicit fallback pin translation, $files marker skew, and the missing artifact-level classloader regression are intentionally not duplicated and are not cleared by this review.
  • Configuration, compatibility, and special paths: Ordinary single-branch, schema-evolution, authorization, rollback/publication, time-travel, fallback, incremental, and FE-planned versus BE-replanned paths were traced. No journal, persistence, transaction, wire-format, or user-configuration compatibility change was introduced. The relevant RBAC boundary is in-model; the deferred authorization transfer itself did not yield another new finding.
  • Concurrency and performance: Volatile publication of the captured table generation is ordered correctly. The added work is planning-time metadata and authorization lookup, with no new hot-loop or execution-path performance blocker found.
  • Tests and evidence: The added unit tests cover several generation, authorization, and snapshot cases, but not the two branch-skew cases above, and the existing packaging-level classloader-test concern remains live. This runner is review-only, so no build or tests were run; conclusions are based on static tracing and current GitHub CI state.
  • Observability and writes: No new persistence/write protocol or logging/metrics contract applies.
  • User focus: No additional review focus was provided; the complete PR was reviewed.

Three bounded review rounds converged on head e692e978351eedf61ad382ff38d37f9a06948029: all reviewers returned NO_NEW_VALUABLE_FINDINGS against this exact two-comment set. This review requests changes on the two new, distinct findings while retaining the existing unresolved threads.

if (range == null || !dataTable.catalogEnvironment().supportsVersionManagement()) {
return incrementalParams;
}
SnapshotManager snapshotManager = dataTable.snapshotManager();

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] Bind this incremental range against each fallback branch

This helper makes both the rewrite decision and endpoint resolution through dataTable.snapshotManager(), which a FallbackReadFileStoreTable delegates to its main branch. If the requested end is older than the main latest, line 352 leaves the timestamp range unchanged even when it extends past the fallback catalog pointer; after loader removal, that branch can include an unpublished or rollback-retained snapshot. If this method does rewrite, Paimon's fallback option rewrite copies the resulting main incremental-between IDs unchanged because it translates only scan.snapshot-id, so disjoint branch histories fail out of range and overlapping IDs select the wrong commits. Please resolve/cap the range separately with both captured branch managers (or reject this composition), and test different branch IDs and catalog times.

return rebuiltSysTable.copy(incrementalParams);
}
if (theScanParams.isOptions()) {
return PaimonScanParams.applyOptions(rebuiltSysTable,

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] Preserve the fallback selector already resolved on FE

The processed FE wrapper has already applied this OPTIONS selector to the catalog-backed fallback pair, where Paimon maps the main snapshot to the fallback branch through that branch's catalog-visible SnapshotManager. Here the code rebuilds an unoptioned pair with CatalogEnvironment.empty() and applies only the main selector again, so FallbackReadFileStoreTable.copy repeats the translation against filesystem state. After fallback rollback/publication skew, FE can plan F7 while the serialized BE table selects retained F8; this also reaches OPTIONS-capable FE-planned system tables such as $ro. Please preserve each selected branch before removing its loader, and add an encode/decode fallback-skew OPTIONS test.

@morningman

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.

Request changes remains warranted on reviewed head 0d83512fab416c1b4b53d7441ea7d876b140e521.

I found no new non-duplicate inline issue, so this review intentionally adds zero inline comments. The current head still does not preserve branch-local catalog state across the catalog-less FE-to-BE handoff:

Critical checkpoint conclusions:

  • Task goal and proof: Removing the metastore loader and co-locating the Paimon FileIO SPI address the reported BE classloading failure on ordinary paths. The goal is not fully met for the advertised fallback/OPTIONS/incremental and catalog-bound metadata paths above. The added FE tests exercise generation capture, reconstruction, authorization, and selected system tables, but not those branch-skew cases or the assembled classloader boundary.
  • Scope and minimality: The production changes are focused on packaging and table reconstruction. The helper boundaries are understandable, but correctness depends on a broad Paimon system-table/fallback lifecycle rather than only local serialization.
  • Concurrency: sysBaseTable is written under the same monitor before volatile publication of paimonSysTable, so readers acquire a matching wrapper/base generation. Scan-node table state is query-local. No new lock ordering or shared-collection race was found.
  • Lifecycle: Capturing the system wrapper and base together fixes refresh-generation skew, and rebuilding fallback branches independently preserves captured schemas. The unresolved lifecycle break is intentional loader removal before every branch-local selector/range has been frozen.
  • Configuration: No Doris configuration item is added or changed; dynamic Paimon options remain relation-local copies.
  • Compatibility and parallel paths: No thrift, persisted metadata, storage format, function symbol, or transaction protocol changes are introduced. I traced data tables, fixed-row and schema-derived system tables, native/JNI consumers, version-managed and plain catalogs, decorated/fallback tables, publication/rollback/empty states, and OPTIONS/incremental modes. The live threads above cover the remaining wrong parallel paths.
  • Conditional checks: The five pin-capable BE-resolved system tables are classified correctly, and $files authorization is transferred branch by branch. The fallback early return in incremental binding and the post-loader OPTIONS reapplication have concrete failure paths already documented in the live threads.
  • Tests and results: This was a review-only environment, so no build or test suite was run. Static shell parsing passed. Current CI shows CheckStyle and repository checks green, but the extension workflow did not exercise the assembled scanner/preload classloaders; that does not prove the runtime packaging contract.
  • Authorization and error handling: Under the repository threat model, limited-user RBAC is in scope. The earlier fallback authorization loss is fixed: main and fallback $files branches are authorized before their loaders are removed. No new silent error or distinct RBAC bypass was found.
  • Observability: The new build guard has specific error messages and the FE retains existing planning timing/error reporting. No additional hot-path metric or log is necessary.
  • Transactions, persistence, writes, and FE-BE variables: Not applicable beyond the existing serialized-table handoff. There is no Doris write, EditLog, failover, or new FE-BE field.
  • Performance and other invariants: Reconstruction, authorization, and snapshot binding occur once per scan initialization rather than per row or split. No distinct CPU, memory, transaction-visibility, or MoW invariant issue was found.

No additional user review focus was provided; the complete PR was reviewed.

@morningman

Copy link
Copy Markdown
Contributor Author

run buildall

@hello-stephen

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

------ Round 1 ----------------------------------
============================================
q1	17638	4055	4001	4001
q2	2372	321	215	215
q3	10249	1443	838	838
q4	4692	467	332	332
q5	7747	865	554	554
q6	209	173	143	143
q7	771	825	640	640
q8	10022	1479	1563	1479
q9	6947	4322	4305	4305
q10	7372	1716	1463	1463
q11	527	346	324	324
q12	765	590	465	465
q13	18167	3368	2669	2669
q14	265	261	251	251
q15	q16	791	782	714	714
q17	1031	1034	1019	1019
q18	6995	5722	5577	5577
q19	1266	1263	1010	1010
q20	843	696	573	573
q21	5897	2694	2500	2500
q22	432	362	302	302
Total cold run time: 104998 ms
Total hot run time: 29374 ms

----- Round 2, with runtime_filter_mode=off -----
============================================
q1	4437	4286	4268	4268
q2	289	312	219	219
q3	4579	4937	4394	4394
q4	2059	2182	1371	1371
q5	4450	4337	4455	4337
q6	252	189	131	131
q7	1795	1643	1556	1556
q8	2406	2210	2111	2111
q9	7645	7675	7727	7675
q10	4678	4658	4294	4294
q11	598	441	392	392
q12	755	759	536	536
q13	3220	3638	3000	3000
q14	288	299	273	273
q15	q16	796	766	625	625
q17	1347	1260	1287	1260
q18	7815	7356	7466	7356
q19	1236	1163	1143	1143
q20	2258	2222	1978	1978
q21	5758	5046	4489	4489
q22	526	465	395	395
Total cold run time: 57187 ms
Total hot run time: 51803 ms

@hello-stephen

Copy link
Copy Markdown
Contributor
TPC-DS: Total hot run time: 177151 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 0d83512fab416c1b4b53d7441ea7d876b140e521, data reload: false

query5	4343	615	474	474
query6	478	229	201	201
query7	4878	579	313	313
query8	362	184	165	165
query9	8763	4098	4084	4084
query10	469	378	303	303
query11	5859	2311	2134	2134
query12	165	103	101	101
query13	1288	580	422	422
query14	6257	5186	4932	4932
query14_1	4279	4236	4217	4217
query15	215	211	175	175
query16	1025	476	438	438
query17	1163	723	578	578
query18	2494	472	350	350
query19	230	193	149	149
query20	118	108	109	108
query21	245	157	131	131
query22	13620	13551	13380	13380
query23	17263	16420	16087	16087
query23_1	16164	16246	16214	16214
query24	7807	1749	1296	1296
query24_1	1291	1271	1288	1271
query25	534	438	363	363
query26	1351	360	216	216
query27	2564	615	387	387
query28	4439	2020	2004	2004
query29	1037	622	469	469
query30	346	278	234	234
query31	1122	1082	958	958
query32	105	63	59	59
query33	508	304	245	245
query34	1194	1114	624	624
query35	775	790	664	664
query36	1042	1013	883	883
query37	170	113	87	87
query38	1878	1723	1635	1635
query39	888	879	854	854
query39_1	836	840	822	822
query40	243	167	140	140
query41	66	65	67	65
query42	94	90	91	90
query43	316	320	278	278
query44	1409	783	765	765
query45	195	183	172	172
query46	1058	1214	731	731
query47	2169	2145	1983	1983
query48	403	431	306	306
query49	585	421	309	309
query50	1075	425	355	355
query51	10683	11643	10386	10386
query52	86	85	75	75
query53	275	292	191	191
query54	276	226	211	211
query55	73	70	65	65
query56	287	290	283	283
query57	1317	1290	1189	1189
query58	286	254	262	254
query59	1588	1626	1400	1400
query60	315	275	264	264
query61	184	172	176	172
query62	548	512	436	436
query63	245	206	206	206
query64	2997	1180	992	992
query65	4707	4615	4645	4615
query66	1857	522	449	449
query67	29219	29093	29034	29034
query68	3470	1585	1014	1014
query69	423	311	268	268
query70	887	821	843	821
query71	378	329	335	329
query72	3020	2744	2400	2400
query73	818	800	444	444
query74	5058	4911	4730	4730
query75	2511	2497	2142	2142
query76	2329	1176	780	780
query77	351	383	281	281
query78	11925	11795	11297	11297
query79	1362	1169	778	778
query80	1297	555	475	475
query81	527	332	297	297
query82	595	158	117	117
query83	377	324	297	297
query84	281	160	130	130
query85	981	611	539	539
query86	411	239	227	227
query87	1818	1812	1737	1737
query88	3722	2845	2818	2818
query89	445	366	324	324
query90	1919	203	192	192
query91	205	189	160	160
query92	61	62	57	57
query93	1750	1508	1028	1028
query94	716	375	319	319
query95	787	489	486	486
query96	1090	847	381	381
query97	2710	2604	2496	2496
query98	214	212	201	201
query99	1088	1104	990	990
Total cold run time: 264398 ms
Total hot run time: 177151 ms

@hello-stephen

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

query1	0.00	0.00	0.00
query2	0.10	0.05	0.04
query3	0.25	0.14	0.14
query4	1.60	0.13	0.13
query5	0.24	0.23	0.22
query6	1.25	1.05	1.06
query7	0.03	0.01	0.01
query8	0.05	0.04	0.04
query9	0.38	0.31	0.32
query10	0.54	0.53	0.55
query11	0.19	0.13	0.14
query12	0.17	0.14	0.14
query13	0.46	0.47	0.48
query14	1.02	1.00	1.01
query15	0.62	0.62	0.59
query16	0.32	0.31	0.33
query17	1.09	1.08	1.06
query18	0.21	0.20	0.21
query19	2.02	1.96	2.01
query20	0.02	0.01	0.02
query21	15.43	0.23	0.13
query22	4.85	0.05	0.05
query23	16.12	0.31	0.12
query24	3.01	0.41	0.31
query25	0.11	0.05	0.04
query26	0.74	0.20	0.14
query27	0.04	0.04	0.04
query28	3.48	0.93	0.53
query29	12.46	4.06	3.27
query30	0.27	0.15	0.16
query31	2.77	0.59	0.32
query32	3.22	0.59	0.49
query33	3.14	3.30	3.26
query34	15.72	4.22	3.51
query35	3.51	3.51	3.52
query36	0.54	0.45	0.40
query37	0.08	0.07	0.06
query38	0.05	0.04	0.03
query39	0.04	0.03	0.03
query40	0.17	0.17	0.15
query41	0.09	0.03	0.03
query42	0.04	0.02	0.03
query43	0.04	0.04	0.03
Total cold run time: 96.48 s
Total hot run time: 24.96 s

@hello-stephen

Copy link
Copy Markdown
Contributor

FE Regression Coverage Report

Increment line coverage 0.00% (0/120) 🎉
Increment coverage report
Complete coverage report

morningman and others added 4 commits July 30, 2026 17:49
… BE reads

Reading an object-store-backed Paimon table over a REST/DLF catalog failed on BE
when resolving the latest snapshot from the filesystem:

  NoClassDefFoundError: org/apache/paimon/fs/FileIOLoader

The paimon OSS/S3 FileIO plugins (paimon-s3 -> S3Loader, paimon-jindo -> JindoLoader)
were bundled in preload-extensions, which sits on BE's JVM system (app) classpath, but
the org.apache.paimon.fs.FileIOLoader interface they implement lives in paimon-common,
bundled only in paimon-scanner's own JniScannerClassLoader. When a BE read resolves the
latest snapshot from the filesystem, RESTTokenFileIO.fileIO() calls FileIO.get() ->
ServiceLoader.load(FileIOLoader.class, ...), which eagerly instantiates every registered
provider. JniScannerClassLoader is a plain parent-first URLClassLoader, so the app
classloader defines S3Loader/JindoLoader and cannot resolve the child-only FileIOLoader
interface -> the whole discovery aborts.

Co-locate the plugins with the FileIOLoader interface: move paimon-s3 / paimon-jindo from
preload-extensions to paimon-scanner. paimon-scanner already carries a complete paimon
runtime, and the assembly's metaInf-services handler merges the plugins' service files with
paimon-common's, so all five FileIOLoader providers (local/hadoop/viewfs/s3/jindo) and the
interface end up in one classloader. The Jindo SDK stays on the app classpath (start_be.sh
adds jindofs to DORIS_CLASSPATH), so paimon-scanner still reaches it via parent delegation.

Both jars must be rebuilt and redeployed: paimon-scanner (now carries the plugins) and
preload-extensions (must stop advertising the un-linkable providers on the shared classpath).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DHwqsiGWNosZCeS4ecK97e
…ld time

The FileIOLoader interface (paimon-common) and every provider implementing it
must sit in the same jar: JniScannerClassLoader delegates parent-first, so a
provider left on the shared preload-extensions (JVM app) classpath cannot
resolve the child-only interface and ServiceLoader discovery aborts at runtime
with "NoClassDefFoundError: FileIOLoader".

Unit tests all run in one classloader and cannot reproduce that, so the
invariant is asserted on the built artifacts instead: paimon-scanner's merged
META-INF/services/org.apache.paimon.fs.FileIOLoader must list S3Loader and
JindoLoader, and preload-extensions must bundle no org/apache/paimon/(s3|jindo)
class at all.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…isting

build.sh runs with `set -eo pipefail`. `unzip -l <jar> | grep -q` closes the
pipe on the first match, so unzip dies of SIGPIPE and the pipeline reports 141
even though grep matched: the preload-extensions guard then took its false
branch and let the forbidden provider through. The listing is 120k entries
(11 MB) against a 64 KB pipe buffer, so any match outside the last few lines
hits this - the guard failed exactly when a provider had leaked in, and stayed
quiet on a clean jar, which is why the build never showed it. Let grep consume
the whole listing instead.

The paimon-scanner guard had a second, unrelated hole: `unzip -p` exits 11 when
the services entry is missing altogether, which under `set -e` killed the build
with "caution: filename not matched" before the guard could explain itself -
its primary failure mode. Tolerate that exit and let the message print.

Also record the fallback-branch limit of the BE-side snapshot pin: for a table
with `scan.fallback-branch`, `FallbackReadFileStoreTable#copyWithoutTimeTravel`
derives the fallback branch's own bound through
`SnapshotManager#earlierOrEqualTimeMills`, and that branch has no catalog loader
left either, so it resolves against the snapshot directory like the other
metadata-only gaps already noted there.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…alog metastore

Every table loaded from a metastore-backed Paimon catalog (HMS / DLF) carries a
Paimon HiveCatalogLoader in its CatalogEnvironment. The BE only reads - via
FE-resolved splits and the object store - and never needs the catalog, yet
deserializing that loader forces the whole Hive metastore stack onto the BE
classpath: HiveConf, the metastore API, and, when a system table resolves its
latest snapshot, even the metastore client (DLF's ProxyMetaStoreClient and its
REST stack).

PaimonScanPlanProvider now serializes a catalog-less table to the BE. A data
table is rebuilt from fileIO / location / schema; a system table is rebuilt over
a catalog-less base so SnapshotManager lists the snapshot directory instead of
calling the metastore. Everything the loader used to do on the BE is done on the
FE first, so dropping it changes no rows:

- authorization: $files re-plans on the BE through DataTableScan#plan(), whose
  Catalog#authTableQuery becomes a permanent allow without the loader, so it is
  run here - per branch on a scan.fallback-branch pair, whose fallback branch
  carries its own CatalogEnvironment.
- snapshot: for the five system tables that pick their snapshot inside the BE
  reader, the catalog-visible snapshot is pinned via copyWithoutTimeTravel, so a
  version-managed (REST / DLF) catalog cannot expose an unpublished or
  rollback-orphaned snapshot. Row-type-following tables ($audit_log, $ro, ...)
  are deliberately left unpinned: pinning would rewind the BE schema.
- incremental range: @incr on $partitions resolves its timestamp endpoints
  against the catalog and hands the BE the explicit id range paimon would have
  computed, except on a fallback pair, where an id range cannot be expressed.
- schema generation: getSysTableHandle builds the wrapper over the base Table
  its handle already carries and keeps that base on the sys handle, so the FE
  and the BE never plan on two different generations. The decorators paimon
  stacks on a loaded table are peeled down to the fallback pair, which must stay
  on top or $ro silently drops every fallback-only partition.
- relation scan params: the @options / @incr copy the FE applied to the original
  wrapper is re-applied to the rebuilt one.

With the FE no longer sending a catalog loader, paimon-scanner does not need the
Hive artifacts on the BE side either; they are removed in the same change.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@morningman

Copy link
Copy Markdown
Contributor Author

run buildall

@hello-stephen

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

------ Round 1 ----------------------------------
============================================
q1	17685	4163	4103	4103
q2	2468	321	202	202
q3	10414	1457	825	825
q4	4740	468	337	337
q5	7704	836	565	565
q6	184	178	137	137
q7	753	823	604	604
q8	9960	1521	1501	1501
q9	7087	4335	4277	4277
q10	7329	1710	1454	1454
q11	865	345	322	322
q12	798	593	468	468
q13	18872	3319	2696	2696
q14	258	258	240	240
q15	q16	784	779	703	703
q17	1030	1000	1047	1000
q18	7270	5718	5631	5631
q19	1797	1244	1025	1025
q20	802	706	567	567
q21	5925	2589	2415	2415
q22	417	352	295	295
Total cold run time: 107142 ms
Total hot run time: 29367 ms

----- Round 2, with runtime_filter_mode=off -----
============================================
q1	4493	4370	4373	4370
q2	288	320	209	209
q3	4526	4935	4396	4396
q4	2038	2144	1369	1369
q5	4372	4570	4459	4459
q6	247	195	134	134
q7	1782	1731	1521	1521
q8	2415	2048	2058	2048
q9	7486	7709	7776	7709
q10	4660	4635	4176	4176
q11	527	401	384	384
q12	748	743	533	533
q13	3267	3601	2887	2887
q14	376	367	265	265
q15	q16	673	698	615	615
q17	1277	1268	1249	1249
q18	7837	7297	7245	7245
q19	1149	1179	1135	1135
q20	2211	2206	1968	1968
q21	5661	4932	4404	4404
q22	510	451	405	405
Total cold run time: 56543 ms
Total hot run time: 51481 ms

@hello-stephen

Copy link
Copy Markdown
Contributor
TPC-DS: Total hot run time: 177636 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 8db5036d2e2db28b10a0d519a2cfb3e1d82ff418, data reload: false

query5	4300	627	471	471
query6	492	219	205	205
query7	4975	626	335	335
query8	350	193	165	165
query9	8774	4120	4108	4108
query10	480	364	306	306
query11	5908	2370	2179	2179
query12	164	104	99	99
query13	1289	604	435	435
query14	6888	5188	4883	4883
query14_1	4318	4211	4223	4211
query15	225	205	179	179
query16	3616	503	474	474
query17	1124	722	574	574
query18	2301	485	354	354
query19	271	196	154	154
query20	114	108	111	108
query21	828	165	144	144
query22	13579	13556	13388	13388
query23	17270	16509	16072	16072
query23_1	16309	16357	16203	16203
query24	7460	1821	1329	1329
query24_1	1345	1331	1296	1296
query25	588	471	384	384
query26	1271	356	211	211
query27	2344	605	393	393
query28	4383	2063	2028	2028
query29	1031	632	493	493
query30	351	271	232	232
query31	1128	1090	994	994
query32	114	63	60	60
query33	550	332	260	260
query34	1208	1145	667	667
query35	764	792	670	670
query36	1032	1009	884	884
query37	183	111	94	94
query38	1894	1672	1636	1636
query39	885	862	838	838
query39_1	850	843	852	843
query40	261	163	163	163
query41	71	68	69	68
query42	98	97	95	95
query43	327	338	290	290
query44	1514	790	774	774
query45	193	184	177	177
query46	1073	1215	755	755
query47	2099	2216	1968	1968
query48	413	416	313	313
query49	566	418	303	303
query50	1110	419	324	324
query51	10760	10515	10444	10444
query52	89	88	74	74
query53	254	290	198	198
query54	283	227	222	222
query55	73	72	65	65
query56	291	292	295	292
query57	1302	1269	1206	1206
query58	298	269	264	264
query59	1617	1640	1462	1462
query60	364	296	255	255
query61	155	150	153	150
query62	541	497	442	442
query63	239	211	202	202
query64	2365	1035	849	849
query65	4729	4647	4637	4637
query66	1688	497	387	387
query67	29344	29199	29098	29098
query68	3031	1499	983	983
query69	454	317	293	293
query70	952	841	811	811
query71	396	335	335	335
query72	3339	2708	2383	2383
query73	881	756	418	418
query74	5110	4907	4794	4794
query75	2564	2499	2128	2128
query76	1904	1182	788	788
query77	358	387	292	292
query78	11852	11922	11429	11429
query79	2596	1206	804	804
query80	1658	575	467	467
query81	533	335	298	298
query82	617	154	118	118
query83	374	327	304	304
query84	283	165	134	134
query85	1006	629	546	546
query86	437	249	245	245
query87	1835	1834	1733	1733
query88	3835	2877	2842	2842
query89	448	371	331	331
query90	1896	207	194	194
query91	202	197	164	164
query92	64	62	54	54
query93	1851	1584	977	977
query94	731	371	309	309
query95	827	508	551	508
query96	1059	809	360	360
query97	2624	2619	2484	2484
query98	217	222	200	200
query99	1167	1113	969	969
Total cold run time: 269050 ms
Total hot run time: 177636 ms

@hello-stephen

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

query1	0.01	0.00	0.00
query2	0.10	0.05	0.05
query3	0.26	0.14	0.14
query4	1.61	0.14	0.13
query5	0.24	0.24	0.23
query6	1.16	0.80	0.79
query7	0.04	0.01	0.01
query8	0.06	0.04	0.04
query9	0.38	0.31	0.31
query10	0.55	0.54	0.54
query11	0.20	0.14	0.13
query12	0.19	0.15	0.14
query13	0.46	0.48	0.47
query14	1.02	1.02	1.00
query15	0.63	0.59	0.60
query16	0.34	0.34	0.32
query17	1.11	1.13	1.07
query18	0.23	0.21	0.21
query19	2.07	2.00	1.97
query20	0.02	0.01	0.01
query21	15.50	0.22	0.12
query22	4.80	0.05	0.06
query23	16.17	0.30	0.12
query24	3.22	0.42	0.33
query25	0.10	0.04	0.03
query26	0.73	0.20	0.15
query27	0.04	0.04	0.03
query28	3.49	0.89	0.54
query29	12.53	4.12	3.30
query30	0.28	0.15	0.15
query31	2.78	0.61	0.31
query32	3.25	0.60	0.49
query33	3.20	3.19	3.16
query34	15.72	4.18	3.53
query35	3.52	3.56	3.54
query36	0.55	0.43	0.43
query37	0.09	0.07	0.07
query38	0.05	0.04	0.04
query39	0.04	0.02	0.03
query40	0.17	0.16	0.15
query41	0.10	0.03	0.03
query42	0.04	0.02	0.02
query43	0.05	0.04	0.03
Total cold run time: 97.1 s
Total hot run time: 24.79 s

@morningman

Copy link
Copy Markdown
Contributor Author

/review

@hello-stephen

Copy link
Copy Markdown
Contributor

FE Regression Coverage Report

Increment line coverage 100% (0/0) 🎉
Increment coverage report
Complete coverage report

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

Automated review result: request changes.

Review completion: COMPLETE. Three bounded review rounds converged on head 8db5036d2e2db28b10a0d519a2cfb3e1d82ff418 and this exact one-comment payload; all three Round 3 reviewers returned NO_NEW_VALUABLE_FINDINGS. No additional user-provided review focus was supplied, so all ten authoritative changed files were reviewed.

Blocking finding

  • The eager fallback-branch authorization can reject a $files query that main completely satisfies, even though Paimon would not invoke fallback's authorized plan() or return fallback files. The inline P1 is distinct from the earlier missing-authorization thread, which applies when fallback does contribute.

Existing threads already cover the fallback OPTIONS/incremental branch-state defects, catalog-visible snapshot/marker limitations, and the missing assembled parent/child classloader regression; those issues were verified at this head and intentionally not duplicated.

Critical checkpoint conclusions

  • Goal, scope, and proof: Removing catalog loaders from the serialized BE table and moving Paimon FileIO providers into the scanner artifact are focused changes that address the reported classloading/metastore reconstruction failure. The accepted P1 shows that loader-free authorization transfer is not yet behaviorally equivalent for every advertised fallback $files path.
  • Correctness, lifecycle, parallel paths, and conditions: Captured system wrapper/base generations, handle copies, decorator peeling, all 17 Paimon system readers, normal/OPTIONS/INCR paths, version-managed and plain catalogs, direct and privilege-decorated fallback tables, and native/JNI consumers were traced. The exact wrapper/base generation survives the production property path; the new unconditional fallback check is the only non-duplicate issue. All other substantiated snapshot/selector cases map to named live threads.
  • Concurrency, configuration, and compatibility: No new shared-state race, lock-order issue, Doris setting, persisted format, storage format, public symbol, Thrift field, or rolling-upgrade contract was introduced. The compatibility impact is the user-visible false denial at the existing serialized-table boundary.
  • Tests and results: The changed FE tests were reviewed statically and cover reconstruction, generation capture, pinning, and eager branch authorization, but not a main-complete/main-allowed/fallback-denied plan. No build or test suite was run because the authoritative review contract prohibits it and this worktree is not initialized; only bash -n build.sh was run as a syntax check. The already-requested assembled-jar classloader test remains a separate live thread.
  • Errors and observability: Existing exceptions and build diagnostics are adequate for explicit failures, but this denial needs a behavioral test rather than a new metric or log. No additional observability defect was found.
  • Transactions, persistence, writes, and variable propagation: Not applicable beyond the existing FE-to-BE serialized Java Table payload; this patch adds no Doris transaction, EditLog/failover, data-write, or new variable-propagation path.
  • Performance and other invariants: The added reconstruction and authorization work is per scan initialization, not per row. No independent material CPU, memory, I/O, or hot-path regression was substantiated.

The final live-state sweep confirmed the PR head/base, normalized diff content, diff position 172, current review threads, and a fully classified ledger with no unresolved candidate.

if (undecorated instanceof FallbackReadFileStoreTable) {
FallbackReadFileStoreTable fallbackReadTable = (FallbackReadFileStoreTable) undecorated;
authorizeBranch(fallbackReadTable.wrapped());
authorizeBranch(fallbackReadTable.fallback());

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] Preserve conditional fallback authorization

FallbackReadScan.plan() always plans the main scan, but invokes fallbackScan.plan() only when listPartitions() finds partitions not completed by main; that listing itself does not authorize. This unconditional call therefore rejects a $files query for a user denied on the fallback branch even when every selected partition is satisfied by main and Paimon would never invoke fallback's authorized plan() or return fallback files. This is the inverse of the earlier missing-auth case: please transfer the fallback authorization with the same branch-use condition (while retaining the check when fallback contributes), and cover a main-complete/main-allowed/fallback-denied case.

@CalvinKirs
CalvinKirs merged commit 687500b into apache:master Jul 31, 2026
32 checks passed
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.

4 participants