Skip to content

[feature](fe) Show inverted index storage format by partition ID - #65776

Closed
hoshinojyunn wants to merge 1 commit into
apache:masterfrom
hoshinojyunn:feat/partition_index_format_observability
Closed

hoshinojyunn wants to merge 1 commit into
apache:masterfrom
hoshinojyunn:feat/partition_index_format_observability

Conversation

@hoshinojyunn

@hoshinojyunn hoshinojyunn commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

What problem does this PR solve?

Issue Number: None

Related PR: None

Problem Summary:

SHOW PARTITION <partition_id> did not expose a table's inverted-index storage format. This made it difficult to inspect the configured format for a specific partition after obtaining its ID.

This change adds InvertedIndexStorageFormat only to SHOW PARTITION <partition_id>. SHOW PARTITIONS FROM <table> intentionally retains its existing result columns; it is used to obtain PartitionId, which can then be supplied to SHOW PARTITION. Tables without a persisted format report V1, matching the legacy BE tablet-schema behavior.

Example:

SHOW PARTITIONS FROM events_full;
PartitionId PartitionName
123456 p20260722
SHOW PARTITION 123456;
PartitionId PartitionName InvertedIndexStorageFormat
123456 p20260722 V3

Release note

SHOW PARTITION <partition_id> now displays InvertedIndexStorageFormat.

Check List (For Author)

  • Test

    • Regression test: regression-test/suites/cloud_p0/test_partition_cloud_inverted_index_format.groovy, regression-test/suites/query_p0/show/test_partition_local_inverted_index_format.groovy
    • Unit Test
    • Manual test (add detailed scripts or steps below)
    • No need to test or manual test. Explain why:
      • This is a refactor/code format and no logic has been changed.
      • Previous test can cover this change.
      • No code files have been changed.
      • Other reason
  • Behavior changed:

    • No.
    • Yes. SHOW PARTITION <partition_id> displays the effective inverted-index storage format.
  • Does this need documentation?

    • No.
    • Yes.

Check List (For Reviewer who merge this PR)

  • Confirm the release note
  • Confirm test cases
  • Confirm document
  • Add branch pick label

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

@hoshinojyunn hoshinojyunn changed the title [feature](fe) Show inverted index storage format for partitions [feat](fe) Show inverted index storage format for partitions Jul 17, 2026
@hoshinojyunn

Copy link
Copy Markdown
Contributor Author

run buildall

? result.getInvertedIndexV3Tablets() : Lists.newArrayList();
if (verbose) {
Map<Long, String> storageFormats = new HashMap<>();
for (long tabletId : result.getV1Tablets()) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

[Blocking] Please preserve the distinction between an unset optional field and a known-empty list.

After this PR enables the command in cloud mode, a new FE can call an old Cloud BE whose BaseBackendService::check_storage_format() returns a non-null result with v1_tablets and v2_tablets unset. Thrift getters return null here, so this loop and the summary .size() calls throw NullPointerException.

An old local BE sets v1_tablets/v2_tablets but does not know the new inverted-index fields. Converting those absent fields to empty lists also makes the non-verbose result report 0, although the format count is unknown.

Please guard all optional lists with isSet..., retain whether the inverted-index fields were present, and return UNKNOWN or a clear unsupported-version error for absent data. Please also add mixed-version tests for both response shapes: old local BE (only v1/v2 set) and old Cloud BE (all lists unset).

@airborne12

Copy link
Copy Markdown
Member

/review

@hello-stephen

Copy link
Copy Markdown
Contributor

Cloud UT Coverage Report

Increment line coverage 🎉

Increment coverage report
Complete coverage report

Category Coverage
Function Coverage 77.62% (1908/2458)
Line Coverage 64.48% (34152/52968)
Region Coverage 64.89% (17570/27076)
Branch Coverage 54.05% (9417/17424)

@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 PR implements the happy path for fresh internal V2/V3 tables and both current BE producers set the append-only Thrift fields, but it is not safe to merge yet. The inline comments cover a Cloud tablet-map data race, cache-only Cloud inventory that silently omits valid tablets, incomplete filter/catalog/type validation, repeated-column predicates that are silently dropped, legacy FE/BE default disagreement, incorrect SQL LIKE semantics, a doubled summary RPC payload, and avoidable O(N log N) FE work. The existing mixed-version optional-field thread remains blocking and was not duplicated.

Critical checkpoint conclusions:

  • Goal and focus: the new columns and current-version local/Cloud happy paths are wired, but rolling-upgrade observability is not correct for legacy metadata, old BE responses, or non-resident Cloud tablets. There was no additional user-provided review focus.
  • Scope and parallel paths: the code is otherwise focused and the local BE classification is consistent for current V1/V2/V3 enums. MaxCompute/Paimon validation is a missed parallel path; the partitions TVF's independent leading-20-column projection remains safe because the new proc cell is appended.
  • Concurrency and lifecycle: the new Cloud RPC reaches an unlocked unordered-map size read concurrent with cache insertion/eviction, and its result depends on volatile cache materialization/restart/eviction lifecycle. No new thread, static-initialization dependency, ownership cycle, or lock-order issue was found beyond that race.
  • Compatibility and persistence: new Thrift IDs are append-only and current producers set all presence bits. New-FE/old-BE absence handling is already covered by discussion r3627008129. Separately, replayed FE property defaults disagree with BE's V1 tablet-schema compatibility rule.
  • Error handling and semantics: external predicates/orders can be accepted and ignored, a numeric RHS leaks NumberFormatException, repeated predicates on the new column overwrite one another, and the newly exposed LIKE route does not implement SQL wildcard semantics.
  • Performance: the response unconditionally duplicates every tablet ID even for count-only output, while verbose output also replaces linear emission with two hash maps plus a TreeSet, adding avoidable wire/deserialization cost, O(N log N) work, and significant boxed heap on large backends.
  • Transactions, data correctness, configuration, and observability: this is a read-only observability change with no transaction, visible-version, delete-bitmap, data-write, EditLog, or configuration mutation. The main correctness risk is misleading or incomplete reported state.
  • Tests: the added tests cover fresh range/list/auto/dynamic V2/V3 equality happy paths. Missing coverage includes mixed versions, legacy replay/V1, empty/restarted/evicted Cloud tablets, concurrent cache mutation, external-catalog rejection, invalid RHS types, repeated-column conjunctions, LIKE wildcards, and large summary/verbose inputs. Per the review-run contract, no build or test was executed locally; conclusions are from static code/control-flow review.

Review status: complete for the current head after full changed-file coverage, independent main verification, duplicate suppression, and normal plus risk-focused convergence.

Comment thread be/src/cloud/cloud_backend_service.cpp Outdated
}

void CloudBackendService::check_storage_format(TCheckStorageFormatResult& result) {
for (const auto& tablet : _engine.tablet_mgr().get_all_tablet()) {

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.

[Blocking] Please take the tablet-map snapshot under one lock. This new RPC calls get_all_tablet(), which evaluates _tablet_map->size() before the locked traverse. TabletMap::size() reads the unordered_map without _mtx, while cache-miss put() and the LRU Value destructor's erase() mutate that same map under _mtx on other workers. An overlapping SHOW request therefore performs an unsynchronized container read/write (undefined behavior); locking traverse afterward is too late. Please have TabletMap build the shared_ptr snapshot, including reserve, under _mtx and add a concurrent enumeration/cache-mutation test.

Comment thread be/src/cloud/cloud_backend_service.cpp Outdated

void CloudBackendService::check_storage_format(TCheckStorageFormatResult& result) {
for (const auto& tablet : _engine.tablet_mgr().get_all_tablet()) {
result.v2_tablets.push_back(tablet->tablet_id());

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.

[Blocking] This Cloud path inventories only tablets currently resident in each BE's volatile CloudTabletMgr cache, not all valid tablets. TabletMap is populated only after get_tablet() materializes a tablet and entries disappear on LRU eviction, so after a clean BE restart—or for a newly created empty/never-read tablet—this command omits IDs that SHOW TABLETS still lists. The new one-BE test writes every target partition first, which masks the gap. Please source the intended inventory from durable FE/meta-service metadata, or make the cache-only/incomplete contract explicit in the output, and add empty/restart/eviction coverage.

partitionInfo.add(partition.getRowCount());
trow.addToColumnValue(new TCell().setLongVal(partition.getRowCount()));

String invertedIndexStorageFormat = olapTable.getInvertedIndexFileStorageFormat().name();

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.

[Blocking] Please resolve the legacy default before exposing it as the partition's storage format. When replayed FE TableProperty metadata lacks inverted_index_storage_format, this getter reports DEFAULT (or V2 when TableProperty is null), but BE's backward-compatibility rule maps a missing TabletSchemaPB field to physical V1. The same upgraded table can therefore show DEFAULT/V2 here and V1 in SHOW TABLET STORAGE FORMAT, and filtering partitions by V1 misses it. Please align the legacy effective value with the BE rule (or report UNKNOWN explicitly) and add replay coverage for metadata without either field.

if (subExpr instanceof ComparisonPredicate) {
if (leftKey.equalsIgnoreCase(FILTER_PARTITION_NAME) || leftKey.equalsIgnoreCase(FILTER_STATE)) {
if (leftKey.equalsIgnoreCase(FILTER_PARTITION_NAME) || leftKey.equalsIgnoreCase(FILTER_STATE)
|| leftKey.equalsIgnoreCase(FILTER_INVERTED_INDEX_STORAGE_FORMAT)) {

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.

[Blocking] Please finish validation for this new string column. First, validate() only special-cases HMS, so MaxCompute and Paimon accept WHERE InvertedIndexStorageFormat = 'V3' even though their schemas lack this column and their handlers ignore filterMap; adding the name to TITLE_NAMES also accepts ORDER BY here while those handlers sort by partition name. Second, the internal path accepts InvertedIndexStorageFormat = 3 because it checks only EqualTo; execution then calls Long.parseLong("V3") and leaks NumberFormatException. Please scope the column to supported schemas, require a string-like RHS, and add external-catalog and invalid-type negative tests.

if (subExpr instanceof ComparisonPredicate) {
if (leftKey.equalsIgnoreCase(FILTER_PARTITION_NAME) || leftKey.equalsIgnoreCase(FILTER_STATE)) {
if (leftKey.equalsIgnoreCase(FILTER_PARTITION_NAME) || leftKey.equalsIgnoreCase(FILTER_STATE)
|| leftKey.equalsIgnoreCase(FILTER_INVERTED_INDEX_STORAGE_FORMAT)) {

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.

[Blocking] Please preserve every predicate in a valid conjunction on this new column. analyzeSubExpression() recursively accepts all AND children, but filterMap is keyed only by column name, so each leaf overwrites the previous one. On a V3 table, WHERE InvertedIndexStorageFormat = 'V2' AND InvertedIndexStorageFormat = 'V3' consequently evaluates only the last equality and returns rows although the conjunction is false. Please either reject repeated-column predicates or retain and evaluate their full conjunction, and add contradictory-equality plus equality/LIKE coverage.

if (!leftKey.equalsIgnoreCase(FILTER_PARTITION_NAME) && !leftKey.equalsIgnoreCase(FILTER_STATE)) {
throw new AnalysisException("Where clause : PartitionName|State like \"p20191012|NORMAL\"");
if (!leftKey.equalsIgnoreCase(FILTER_PARTITION_NAME) && !leftKey.equalsIgnoreCase(FILTER_STATE)
&& !leftKey.equalsIgnoreCase(FILTER_INVERTED_INDEX_STORAGE_FORMAT)) {

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.

[Blocking] The newly allowed LIKE path does not have SQL LIKE semantics. PartitionsProcDir.like() converts % but never converts the SQL _ single-character wildcard, and it leaves Java-regex metacharacters active before String.matches(). Thus InvertedIndexStorageFormat LIKE 'V_' returns no V1/V2/V3 rows, while 'V[123]' can match even though brackets should be literals. Please use the standard Doris LIKE matcher (including escape handling) and add wildcard/regex-literal tests for this column.

List<Long> invertedIndexV3Tablets = result.isSetInvertedIndexV3Tablets()
? result.getInvertedIndexV3Tablets() : Lists.newArrayList();
if (verbose) {
Map<Long, String> storageFormats = new HashMap<>();

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.

[Performance] Please avoid sorting and triplicating the full tablet inventory in verbose mode. The old path emitted the V1/V2 lists directly in O(N); this path now boxes every ID into a base HashMap, another inverted-format HashMap, and a TreeSet, adding O(N log N) work and substantial FE heap for large backends without an ordering contract or test dependency. A single inverted-format lookup plus the existing direct V1/V2 emission loops provides the new column while keeping the previous complexity.

Comment thread gensrc/thrift/BackendService.thrift Outdated
struct TCheckStorageFormatResult {
1: optional list<i64> v1_tablets;
2: optional list<i64> v2_tablets;
3: optional list<i64> inverted_index_v1_tablets;

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.

[Performance] Please avoid doubling the full tablet-ID RPC payload for summary mode. Every producer now puts each tablet ID once in a legacy base-format list and again in a new inverted-format list, but this no-argument RPC is called before the FE branches on verbose, and the non-verbose path uses only the five list sizes. A summary for N tablets therefore still allocates, serializes, transfers, and deserializes 2N IDs just to print counts, potentially hitting the Thrift message limit on large backends. Please add a count-oriented mode/response or a compact representation that preserves rolling-upgrade compatibility, with scale coverage.

@hello-stephen

Copy link
Copy Markdown
Contributor

FE UT Coverage Report

Increment line coverage 7.32% (3/41) 🎉
Increment coverage report
Complete coverage report

@hello-stephen

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

------ Round 1 ----------------------------------
============================================
q1	17728	4115	4051	4051
q2	2001	339	201	201
q3	10951	1419	842	842
q4	4750	474	338	338
q5	8400	856	561	561
q6	355	171	138	138
q7	846	819	600	600
q8	10551	1714	1577	1577
q9	5746	4375	4382	4375
q10	6738	1774	1461	1461
q11	523	366	347	347
q12	740	578	458	458
q13	18100	3268	2742	2742
q14	265	260	242	242
q15	q16	788	774	708	708
q17	1037	948	954	948
q18	6766	5697	5540	5540
q19	1190	1363	1091	1091
q20	819	690	562	562
q21	5638	2614	2412	2412
q22	427	343	295	295
Total cold run time: 104359 ms
Total hot run time: 29489 ms

----- Round 2, with runtime_filter_mode=off -----
============================================
q1	4443	4314	4356	4314
q2	292	326	208	208
q3	4579	4924	4371	4371
q4	2071	2176	1364	1364
q5	4386	4267	4277	4267
q6	237	175	125	125
q7	2248	1920	1564	1564
q8	2463	2147	2114	2114
q9	7892	7731	7830	7731
q10	4667	4657	4198	4198
q11	563	453	471	453
q12	734	762	546	546
q13	3272	3469	2953	2953
q14	327	333	279	279
q15	q16	702	723	651	651
q17	1373	1339	1381	1339
q18	8170	7447	6795	6795
q19	1103	1096	1120	1096
q20	2216	2198	1941	1941
q21	5221	4526	4316	4316
q22	526	467	411	411
Total cold run time: 57485 ms
Total hot run time: 51036 ms

@hello-stephen

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

query5	4312	614	491	491
query6	463	220	216	216
query7	4894	639	356	356
query8	335	185	181	181
query9	8770	4033	4055	4033
query10	470	375	338	338
query11	5915	2325	2090	2090
query12	153	104	103	103
query13	1249	597	441	441
query14	6242	5238	4908	4908
query14_1	4246	4241	4245	4241
query15	220	206	183	183
query16	1009	476	466	466
query17	1107	725	612	612
query18	2450	464	338	338
query19	202	181	141	141
query20	117	104	104	104
query21	227	159	130	130
query22	13633	13439	13333	13333
query23	17368	16510	16192	16192
query23_1	16239	16250	16215	16215
query24	7530	1740	1302	1302
query24_1	1281	1267	1253	1253
query25	536	432	369	369
query26	1327	344	213	213
query27	2658	572	383	383
query28	4526	1996	1960	1960
query29	1103	616	468	468
query30	343	269	228	228
query31	1120	1083	973	973
query32	109	61	61	61
query33	535	319	237	237
query34	1169	1105	641	641
query35	758	789	659	659
query36	1173	1204	1030	1030
query37	153	101	98	98
query38	1885	1709	1674	1674
query39	876	866	828	828
query39_1	834	839	845	839
query40	243	165	140	140
query41	67	61	62	61
query42	91	94	91	91
query43	323	339	275	275
query44	1405	755	766	755
query45	196	182	171	171
query46	1051	1209	695	695
query47	2159	2158	2030	2030
query48	411	434	311	311
query49	592	433	312	312
query50	1077	440	341	341
query51	10798	10566	10627	10566
query52	86	96	74	74
query53	271	273	201	201
query54	293	263	238	238
query55	76	79	68	68
query56	306	342	297	297
query57	1323	1300	1213	1213
query58	288	273	254	254
query59	1578	1653	1441	1441
query60	306	299	272	272
query61	182	173	171	171
query62	538	500	434	434
query63	242	208	213	208
query64	2964	1146	889	889
query65	4707	4593	4657	4593
query66	1824	500	370	370
query67	29210	29193	28975	28975
query68	3251	1561	1046	1046
query69	396	312	252	252
query70	1075	967	933	933
query71	351	321	307	307
query72	2995	2642	2460	2460
query73	853	739	450	450
query74	5066	4915	4710	4710
query75	2538	2490	2124	2124
query76	2324	1207	784	784
query77	359	388	281	281
query78	11890	11826	11344	11344
query79	1391	1177	774	774
query80	1276	551	455	455
query81	524	335	286	286
query82	627	151	119	119
query83	376	319	299	299
query84	299	159	131	131
query85	983	599	539	539
query86	436	304	282	282
query87	1845	1815	1754	1754
query88	3700	2786	2752	2752
query89	431	371	336	336
query90	1935	206	199	199
query91	205	191	165	165
query92	63	66	55	55
query93	1648	1576	976	976
query94	724	339	319	319
query95	797	570	502	502
query96	1043	776	349	349
query97	2676	2606	2522	2522
query98	211	210	204	204
query99	1095	1111	979	979
Total cold run time: 263789 ms
Total hot run time: 177411 ms

@hello-stephen

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

query1	0.00	0.00	0.01
query2	0.14	0.08	0.08
query3	0.36	0.24	0.24
query4	1.62	0.24	0.25
query5	0.32	0.31	0.31
query6	1.17	0.67	0.67
query7	0.03	0.01	0.00
query8	0.09	0.07	0.07
query9	0.51	0.38	0.39
query10	0.58	0.57	0.59
query11	0.30	0.19	0.18
query12	0.31	0.18	0.18
query13	0.55	0.53	0.53
query14	0.94	0.91	0.94
query15	0.67	0.58	0.61
query16	0.38	0.39	0.38
query17	1.03	1.02	0.99
query18	0.31	0.30	0.30
query19	1.91	1.79	1.84
query20	0.02	0.02	0.02
query21	15.42	0.39	0.31
query22	4.74	0.15	0.14
query23	15.85	0.50	0.30
query24	2.39	0.60	0.43
query25	0.15	0.10	0.09
query26	0.74	0.27	0.21
query27	0.11	0.10	0.10
query28	3.53	0.92	0.53
query29	12.44	4.32	3.33
query30	0.38	0.26	0.25
query31	2.76	0.62	0.33
query32	3.24	0.59	0.47
query33	2.89	2.97	2.98
query34	15.74	4.08	3.38
query35	3.29	3.25	3.27
query36	0.64	0.53	0.51
query37	0.12	0.08	0.09
query38	0.08	0.07	0.06
query39	0.08	0.06	0.06
query40	0.19	0.17	0.17
query41	0.13	0.09	0.08
query42	0.09	0.06	0.06
query43	0.07	0.07	0.07
Total cold run time: 96.31 s
Total hot run time: 25.6 s

@hello-stephen

Copy link
Copy Markdown
Contributor

BE Regression && UT Coverage Report

Increment line coverage 0.00% (0/39) 🎉

Increment coverage report
Complete coverage report

Category Coverage
Function Coverage 75.00% (30565/40754)
Line Coverage 59.20% (337347/569829)
Region Coverage 56.00% (283402/506048)
Branch Coverage 57.23% (125728/219692)

@morrySnow morrySnow changed the title [feat](fe) Show inverted index storage format for partitions [feat](inverted index) Show inverted index storage format for partitions Jul 22, 2026
@hoshinojyunn
hoshinojyunn force-pushed the feat/partition_index_format_observability branch from f803007 to f24f338 Compare July 22, 2026 09:42
### What problem does this PR solve?

Issue Number: None

Related PR: None

Problem Summary: SHOW PARTITION <partition_id> did not expose the inverted-index storage format configured for the table that owns the partition. Add InvertedIndexStorageFormat to the SHOW PARTITION result, while keeping SHOW PARTITIONS unchanged so it continues to provide the PartitionId used by the new inspection path. Resolve absent or DEFAULT persisted values to V1, matching the legacy BE tablet-schema behavior.

### Release note

SHOW PARTITION <partition_id> now displays InvertedIndexStorageFormat.

### Check List (For Author)

- Test:
    - Regression test: regression-test/suites/cloud_p0/test_partition_cloud_inverted_index_format.groovy
- Behavior changed: Yes (SHOW PARTITION <partition_id> displays the effective inverted-index storage format)
- Does this need documentation: No
@hoshinojyunn
hoshinojyunn force-pushed the feat/partition_index_format_observability branch from f24f338 to c3ba54f Compare July 22, 2026 10:18
@hoshinojyunn hoshinojyunn changed the title [feat](inverted index) Show inverted index storage format for partitions [feature](fe) Show inverted index storage format by partition ID Jul 22, 2026
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