Skip to content

[fix](metrics) Escape label values in the Prometheus output of FE - #69837

Merged
morningman merged 2 commits into
apache:masterfrom
morningman:changelog-monitor-t8
Nov 2, 2026
Merged

morningman merged 2 commits into
apache:masterfrom
morningman:changelog-monitor-t8

Conversation

@morningman

@morningman morningman commented Oct 29, 2026 •

Copy link
Copy Markdown
Contributor

What problem does this PR solve?

Issue Number: #69907

Related PR: None

Problem Summary:

In short: the FE writes metric label values into its Prometheus output without escaping them. A table name may contain a backslash, a double quote or a line feed, and in cloud mode the Master FE labels four per-table series with the table name, so a single such table leaves the Master's whole /metrics response outside the text format, and Prometheus keeps no sample of a scrape it cannot parse. This PR escapes every label value that is not a literal through one function, as the format requires. Ordinary names are written exactly as before.

Background

In the Prometheus text format a label value is written between double quotes, and a backslash, a double quote or a line feed inside it must be written as \\, \" and \n; no other character is escaped. A scrape whose response does not parse fails as a whole.

With enable_unicode_name_support, which is on by default, a table name may contain all three characters: FeNameFormat.UNICODE_TABLE_NAME_REGEX only requires the last character not to be white space. A local cluster with default settings accepts tables named `a"b`, `a\b` and one with a line feed in the middle. The names of databases, users, workload groups, jobs and catalogs keep their restricted patterns.

The problem, and what it cost

  • PrometheusMetricVisitor concatenates label values as they are, in every place that writes one: the labels of registered metrics (visit), both kinds of histograms, the cloud per-table series (visitCloudTableStats), the workload group series and the JVM GC series.
  • In cloud mode the Master FE exports doris_fe_table_data_size, doris_fe_table_rowset_count, doris_fe_table_segment_count and doris_fe_table_row_count for up to prom_output_table_metrics_limit (10000) tables, labeled with db_name and table_name. One table whose name holds one of the three characters makes the response invalid, and every metric of the Master FE is lost while that table is exported.
  • The coming monitoring of table streams and IVM materialized views labels series with the names of materialized views and streams, which would run into the same problem in both modes.

How this PR fixes it

  • escapeLabelValue writes a backslash, a double quote and a line feed as \\, \" and \n and leaves every other character alone, which is the full set the format requires.
  • Every label value that is not a literal goes through it: the two histogram paths and visit, the database and table names of the per-table series (escaped once per table, not once per series), the workload group name and the GC name. The JVM GC and workload group names cannot hold these characters today; they go through the same function so that no label value in the class is written raw.
  • A name without these characters comes back as the same string, so the output of ordinary names does not change, byte for byte, and costs no extra allocation (String.replace returns the string itself when nothing matches).
  • A null label value is written as null, as the concatenation without escaping wrote it. Label values can be null: a table warm-up job has no source cluster, so the series of its start and finish times carry a null src_cluster_name.

Results

What the cloud per-table series is written as for each table name, and what promtool check metrics (Prometheus v3.2.0) says about it:

Table name Before this PR promtool After this PR promtool
t1 table_name="t1" OK table_name="t1" OK
a"b table_name="a"b" unexpected end of label value "a" table_name="a\"b" OK
a\b table_name="a\b" invalid escape sequence '\b' table_name="a\\b" OK
a\ table_name="a\" label value "a\"} 100" contains unescaped new-line table_name="a\\" OK
a, line feed, b the sample split over two lines label value "a" contains unescaped new-line table_name="a\nb" OK

A parser that follows the format turns each escaped value back into the table name itself.

Classes, and how they call each other

  • PrometheusMetricVisitor: the new private escapeLabelValue, called wherever a label value is written.
  • MetricsTest: testPrometheusVisitorEscapesLabelValues (a gauge with an escaped and a plain label, and both kinds of histograms), testCloudTableStatsEscapeTableName (the four per-table series, with a mocked CloudTabletStatMgr) and testPrometheusVisitorWritesNullLabelValue (a gauge and a labeled histogram with a null label value).
MetricsAction (/metrics) -> MetricRepo.getMetric(PrometheusMetricVisitor)
  |-- visitJvm                  GC name            -> escapeLabelValue
  |-- DORIS_METRIC_REGISTER.accept -> visit         labels -> escapeLabelValue
  |-- visitHistograms -> visitHistogram (both)      labels -> escapeLabelValue
  |-- visitCloudTableStats      db / table name    -> escapeLabelValue
  `-- visitWorkloadGroup        group name         -> escapeLabelValue

Not changed: the JSON output (type=json), which builds its own strings and would need JSON escaping instead; the core output (type=core), which writes no labels; the metric names and the # HELP texts, which are literals.

Release note

In cloud mode, a table whose name contains a backslash, a double quote or a line feed no longer makes the FE /metrics output unparsable for Prometheus.

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:
      • 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

    Unit test: MetricsTest, 21 cases including the three new ones; the two escaping cases fail on the code before this PR, and the null case fails with a NullPointerException without the null handling. Manual: the three table names above created on a local shared-nothing cluster with default settings, and the samples in the table checked with promtool check metrics.

  • Behavior changed:

    • No.
    • Yes.

    A label value that contains a backslash, a double quote or a line feed is escaped in the Prometheus output; every other output is unchanged.

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

@morningman

Copy link
Copy Markdown
Contributor Author

/review

@morningman

Copy link
Copy Markdown
Contributor Author

run buildall

@github-actions

Copy link
Copy Markdown
Contributor

Codex automated review failed and did not complete.

Error: Selected model is at capacity. Please try a different model.
Workflow run: https://github.com/apache/doris/actions/runs/40856334553

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

@hello-stephen

Copy link
Copy Markdown
Contributor

FE UT Coverage Report

Increment line coverage 94.74% (18/19) 🎉
Increment coverage report
Complete coverage report

@hello-stephen

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

------ Round 1 ----------------------------------
============================================
q1	17653	5119	5052	5052
q2	2131	316	242	242
q3	10252	1365	816	816
q4	4683	464	337	337
q5	7440	786	528	528
q6	186	187	151	151
q7	734	790	600	600
q8	9248	1326	1067	1067
q9	5679	4233	4180	4180
q10	6830	1274	1031	1031
q11	436	243	227	227
q12	646	402	290	290
q13	18084	3620	3045	3045
q14	277	272	239	239
q15	q16	721	719	675	675
q17	1186	745	589	589
q18	6944	6165	6090	6090
q19	1103	970	596	596
q20	395	332	217	217
q21	4900	2407	2417	2407
q22	414	318	276	276
Total cold run time: 99942 ms
Total hot run time: 28655 ms

----- Round 2, with runtime_filter_mode=off -----
============================================
q1	5949	5917	5909	5909
q2	729	600	569	569
q3	5675	5672	5407	5407
q4	2013	2180	1566	1566
q5	5789	5728	5632	5632
q6	238	186	143	143
q7	2238	2001	1856	1856
q8	2834	2470	2501	2470
q9	7497	7471	7459	7459
q10	3609	3582	3113	3113
q11	582	416	399	399
q12	707	663	525	525
q13	3246	3570	3024	3024
q14	287	288	263	263
q15	q16	659	695	621	621
q17	7688	7025	6852	6852
q18	12999	12178	12855	12178
q19	919	774	777	774
q20	2116	2136	1951	1951
q21	5689	4665	4734	4665
q22	511	446	410	410
Total cold run time: 71974 ms
Total hot run time: 65786 ms

@hello-stephen

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

query5	4306	596	449	449
query6	416	208	192	192
query7	4806	447	232	232
query8	325	162	148	148
query9	8800	3225	3223	3223
query10	459	311	267	267
query11	6045	3890	3597	3597
query12	148	93	90	90
query13	1253	448	355	355
query14	6121	4022	3687	3687
query14_1	3428	3469	3434	3434
query15	207	198	185	185
query16	950	439	410	410
query17	868	679	549	549
query18	2427	434	319	319
query19	197	171	136	136
query20	89	85	82	82
query21	217	131	109	109
query22	12952	13074	12952	12952
query23	13413	12932	12609	12609
query23_1	12717	12532	12429	12429
query24	7366	872	481	481
query24_1	492	488	491	488
query25	538	417	368	368
query26	1272	257	148	148
query27	2753	441	274	274
query28	4617	2061	2062	2061
query29	1586	614	476	476
query30	314	227	191	191
query31	1008	929	822	822
query32	143	97	93	93
query33	532	307	267	267
query34	887	879	513	513
query35	736	754	649	649
query36	849	827	735	735
query37	130	101	84	84
query38	1779	1698	1645	1645
query39	803	797	744	744
query39_1	730	721	752	721
query40	218	121	99	99
query41	72	70	66	66
query42	90	89	87	87
query43	355	368	317	317
query44	1356	705	714	705
query45	194	188	173	173
query46	855	986	572	572
query47	2797	2951	2929	2929
query48	309	320	232	232
query49	591	393	282	282
query50	684	272	209	209
query51	10487	10496	10347	10347
query52	83	80	71	71
query53	184	212	156	156
query54	233	205	174	174
query55	73	67	64	64
query56	234	228	215	215
query57	1639	1637	1573	1573
query58	292	260	255	255
query59	2144	2193	2015	2015
query60	282	253	226	226
query61	145	145	134	134
query62	381	329	293	293
query63	183	157	172	157
query64	2729	942	745	745
query65	3344	3275	3275	3275
query66	1777	415	304	304
query67	20005	20109	19785	19785
query68	2984	1042	627	627
query69	391	299	254	254
query70	863	814	761	761
query71	296	240	202	202
query72	2711	2601	2260	2260
query73	524	531	303	303
query74	4613	4539	4359	4359
query75	2314	2273	1909	1909
query76	2312	1017	595	595
query77	357	379	272	272
query78	9786	9810	9083	9083
query79	1030	876	531	531
query80	1260	417	341	341
query81	603	319	280	280
query82	621	131	102	102
query83	314	212	181	181
query84	311	122	98	98
query85	852	422	364	364
query86	398	244	232	232
query87	1981	1921	1775	1775
query88	3843	2772	2753	2753
query89	332	283	249	249
query90	1918	174	175	174
query91	166	158	124	124
query92	100	101	83	83
query93	1069	1021	583	583
query94	637	296	273	273
query95	561	391	309	309
query96	695	487	241	241
query97	2425	2414	2310	2310
query98	162	151	157	151
query99	764	755	656	656
Total cold run time: 235449 ms
Total hot run time: 151529 ms

@hello-stephen

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

query1	0.01	0.01	0.00
query2	0.09	0.05	0.05
query3	0.26	0.08	0.09
query4	1.61	0.08	0.10
query5	0.24	0.24	0.24
query6	1.20	0.68	0.68
query7	0.04	0.00	0.01
query8	0.05	0.04	0.03
query9	0.40	0.35	0.36
query10	0.57	0.57	0.60
query11	0.20	0.14	0.14
query12	0.19	0.15	0.15
query13	0.49	0.51	0.49
query14	1.03	1.01	1.00
query15	0.64	0.62	0.62
query16	0.36	0.35	0.35
query17	1.12	1.14	1.16
query18	0.20	0.19	0.19
query19	2.11	1.97	1.98
query20	0.02	0.02	0.01
query21	15.44	0.19	0.13
query22	4.99	0.05	0.05
query23	16.19	0.33	0.12
query24	3.04	0.64	0.28
query25	0.09	0.08	0.05
query26	0.82	0.21	0.15
query27	0.05	0.05	0.04
query28	3.81	0.82	0.37
query29	12.50	4.28	3.38
query30	0.29	0.13	0.09
query31	2.78	0.56	0.33
query32	3.23	0.60	0.52
query33	3.28	3.18	3.23
query34	15.53	4.03	3.42
query35	3.37	3.36	3.35
query36	0.57	0.44	0.44
query37	0.09	0.07	0.06
query38	0.05	0.04	0.04
query39	0.04	0.03	0.03
query40	0.18	0.16	0.14
query41	0.09	0.03	0.03
query42	0.04	0.03	0.02
query43	0.05	0.04	0.03
Total cold run time: 97.35 s
Total hot run time: 24.39 s

@hello-stephen

Copy link
Copy Markdown
Contributor

FE Regression Coverage Report

Increment line coverage 94.74% (18/19) 🎉
Increment coverage report
Complete coverage report

### What problem does this PR solve?

Issue Number: None

Related PR: None

Problem Summary: The Prometheus text format requires a backslash, a
double quote and a line feed inside a label value to be written as
`\\`, `\"` and `\n`. `PrometheusMetricVisitor` wrote label values as
they were. A table name may contain any of the three characters
(`enable_unicode_name_support` is on by default), and in cloud mode the
Master FE exports four per-table series labeled with the table name
(`doris_fe_table_data_size` and the rowset, segment and row counts).
The series of one such table left the `/metrics` response of the
Master outside the text format (`promtool check metrics` rejects each
of the three characters written raw), and Prometheus keeps no sample of
a scrape it cannot parse.

Every label value that is not a literal now goes through one function,
`escapeLabelValue`: the labels of registered metrics and of both kinds
of histograms, the per-table series, the workload group series and the
JVM GC series. Ordinary names are written as before. The coming
monitoring of table streams and IVM materialized views labels series
with their names and will render them through the same function.

### Release note

In cloud mode, a table whose name contains a backslash, a double quote
or a line feed no longer makes the FE `/metrics` output unparsable for
Prometheus.

### Check List (For Author)

- Test: Unit Test
    - `MetricsTest`: a gauge and both kinds of histograms whose label
      value holds all three characters, and the cloud per-table series
      of such a table; the existing cases pin the unchanged output of
      ordinary names.
- Behavior changed: Yes. Label values that contain a backslash, a
  double quote or a line feed are escaped in the Prometheus output;
  nothing else changes.
- Does this need documentation: No

Co-Authored-By: Claude Opus 5.5 <[email protected]>
@morningman
morningman force-pushed the changelog-monitor-t8 branch from 423f437 to 8a9f618 Compare October 31, 2026 01:51
@morningman

Copy link
Copy Markdown
Contributor Author

run buildall

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

Summary

The label escaping covers all changed Prometheus rendering paths and correctly handles backslashes, quotes, and line feeds. It also introduces one P1 regression: table warm-up jobs register a null source-cluster label, and the new helper dereferences that null while rendering the public /metrics endpoint. The inline comment below describes the complete trigger and fix direction.

I traced the endpoint from MetricsAction through MetricRepo.getMetric, the metric registry, histogram dispatch, cloud table statistics, workload-group output, and the warm-up lifecycle. The visitor is otherwise stateless and adds no lock or concurrency changes; cloud statistics are published with non-null catalog names, and the added test restores its mutated global state. The histogram delimiter behavior considered during review predates this patch and is not a new defect. There are no existing blocking inline findings and no additional user focus items.

Testing was inspected but not executed, as required by the static-only review contract. The review converged after 2 bounded rounds with complete changed-file coverage and one verified finding.

Review converged after 2 round(s).

List<MetricLabel> labels = metric.getLabels();
if (!labels.isEmpty()) {
sb.append("{");
List<String> labelStrs = labels.stream().map(l -> l.getKey() + "=\"" + l.getValue()

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] Null source cluster in legacy table warm-up metrics crashes /metrics

Table warm-up jobs use a constructor that sets srcClusterName to null. When such a job is cancelled or times out, updateClusterWarmUpJobLastFinishTime registers src_cluster_name=null. The new visit() call invokes escapeLabelValue(null), whose value.replace(...) throws; MetricRepo.getMetric and MetricsAction do not catch it, so the entire public /metrics response fails after a normal table-job cancellation. Please preserve the old null rendering or normalize nullable label values before escaping.

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 4ddeac2: escapeLabelValue now writes a null value as null, as the concatenation without escaping did, so every value without a backslash, a double quote or a line feed is written exactly as before. MetricsTest#testPrometheusVisitorWritesNullLabelValue writes a gauge and a labeled histogram with a null label value; without the fix it fails with a NullPointerException.

The trigger is wider than a cancellation. runPendingJob calls updateClusterWarmUpJobLatestStartTime with the null srcClusterName each time a table warm-up job starts, and neither per-job time series is ever removed from the registry. So in cloud mode the first table warm-up job to start would have made every scrape of the Master FE's /metrics fail until the FE restarted. The other producers are covered too: the registry already accepts a null label value (computeLabelId maps it to an empty string), and the fix sits in the one function every label value goes through.

@hello-stephen

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

------ Round 1 ----------------------------------
============================================
q1	17582	5153	5166	5153
q2	2121	314	252	252
q3	10252	1370	827	827
q4	4674	455	333	333
q5	7432	790	523	523
q6	175	177	150	150
q7	739	801	595	595
q8	9249	1348	1034	1034
q9	5460	4198	4190	4190
q10	6825	1284	1030	1030
q11	425	241	222	222
q12	637	391	273	273
q13	18117	3496	3012	3012
q14	270	278	251	251
q15	q16	713	718	665	665
q17	1183	751	580	580
q18	7038	6148	6079	6079
q19	1111	954	581	581
q20	389	333	222	222
q21	5105	2133	2248	2133
q22	344	291	249	249
Total cold run time: 99841 ms
Total hot run time: 28354 ms

----- Round 2, with runtime_filter_mode=off -----
============================================
q1	5580	5575	5595	5575
q2	655	549	530	530
q3	5191	5374	4908	4908
q4	1979	2140	1553	1553
q5	5246	5152	5484	5152
q6	265	210	155	155
q7	2376	2040	1814	1814
q8	2983	2680	2693	2680
q9	8062	7998	7990	7990
q10	3816	3643	3281	3281
q11	642	467	433	433
q12	712	687	511	511
q13	3523	3851	3360	3360
q14	337	340	286	286
q15	q16	680	744	633	633
q17	8357	7075	7064	7064
q18	13019	12142	12854	12142
q19	914	790	794	790
q20	2136	2155	1907	1907
q21	5564	4542	4616	4542
q22	504	439	387	387
Total cold run time: 72541 ms
Total hot run time: 65693 ms

@hello-stephen

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

query5	4316	576	430	430
query6	426	207	185	185
query7	4799	442	245	245
query8	321	166	161	161
query9	8792	3161	3107	3107
query10	485	306	253	253
query11	6034	3839	3573	3573
query12	140	89	89	89
query13	1270	433	309	309
query14	6077	3922	3645	3645
query14_1	3380	3483	3375	3375
query15	207	208	182	182
query16	945	446	414	414
query17	857	668	555	555
query18	2407	411	298	298
query19	197	159	126	126
query20	81	82	80	80
query21	211	132	104	104
query22	12903	12968	12774	12774
query23	13374	12688	12301	12301
query23_1	12522	12432	12515	12432
query24	7112	813	475	475
query24_1	483	468	465	465
query25	509	398	340	340
query26	818	252	133	133
query27	2762	438	271	271
query28	4582	2000	2003	2000
query29	1352	573	437	437
query30	294	221	179	179
query31	1004	921	806	806
query32	144	91	85	85
query33	513	285	233	233
query34	873	867	517	517
query35	738	755	646	646
query36	825	792	735	735
query37	124	98	77	77
query38	1769	1779	1658	1658
query39	791	802	740	740
query39_1	726	725	739	725
query40	201	116	92	92
query41	66	63	62	62
query42	88	87	78	78
query43	349	365	319	319
query44	1324	690	694	690
query45	177	176	166	166
query46	836	973	572	572
query47	2774	3037	2724	2724
query48	301	311	240	240
query49	567	406	301	301
query50	706	276	210	210
query51	10534	10403	10367	10367
query52	82	81	70	70
query53	187	210	153	153
query54	238	219	191	191
query55	69	75	64	64
query56	252	229	219	219
query57	1653	1634	1560	1560
query58	286	271	268	268
query59	2189	2217	1965	1965
query60	291	255	246	246
query61	176	167	170	167
query62	397	342	309	309
query63	192	163	163	163
query64	2248	1063	945	945
query65	3318	3249	3252	3249
query66	1749	420	302	302
query67	19975	20210	20151	20151
query68	2934	997	619	619
query69	385	285	250	250
query70	853	770	749	749
query71	299	239	207	207
query72	2694	2537	2310	2310
query73	527	528	293	293
query74	4633	4549	4315	4315
query75	2354	2317	1910	1910
query76	2320	1031	589	589
query77	347	370	264	264
query78	9773	9722	9162	9162
query79	980	894	533	533
query80	1234	424	338	338
query81	586	311	279	279
query82	607	134	103	103
query83	272	190	180	180
query84	296	128	98	98
query85	844	431	372	372
query86	391	277	232	232
query87	1980	1976	1787	1787
query88	3824	2767	2751	2751
query89	328	282	241	241
query90	1981	168	164	164
query91	161	148	127	127
query92	97	81	91	81
query93	1033	1007	568	568
query94	642	291	279	279
query95	572	391	304	304
query96	690	497	238	238
query97	2428	2407	2332	2332
query98	166	155	149	149
query99	766	760	654	654
Total cold run time: 233244 ms
Total hot run time: 150902 ms

@hello-stephen

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

query1	0.01	0.01	0.01
query2	0.09	0.05	0.05
query3	0.27	0.09	0.09
query4	1.61	0.10	0.10
query5	0.24	0.24	0.24
query6	1.17	0.68	0.66
query7	0.03	0.01	0.00
query8	0.05	0.04	0.04
query9	0.40	0.35	0.34
query10	0.59	0.61	0.61
query11	0.21	0.14	0.14
query12	0.20	0.15	0.15
query13	0.50	0.50	0.50
query14	1.03	1.01	1.02
query15	0.63	0.61	0.61
query16	0.32	0.34	0.37
query17	1.15	1.15	1.12
query18	0.20	0.18	0.18
query19	2.07	2.01	1.93
query20	0.02	0.01	0.02
query21	15.48	0.20	0.13
query22	4.87	0.05	0.06
query23	16.31	0.32	0.12
query24	2.90	0.58	0.47
query25	0.10	0.06	0.07
query26	1.31	0.21	0.14
query27	0.05	0.05	0.04
query28	2.90	0.76	0.39
query29	12.48	4.01	3.19
query30	0.29	0.13	0.11
query31	2.77	0.56	0.32
query32	3.23	0.59	0.50
query33	3.25	3.17	3.25
query34	15.35	3.98	3.40
query35	3.34	3.36	3.33
query36	0.56	0.45	0.44
query37	0.10	0.06	0.06
query38	0.05	0.04	0.04
query39	0.04	0.03	0.03
query40	0.18	0.15	0.15
query41	0.09	0.03	0.03
query42	0.04	0.03	0.03
query43	0.04	0.04	0.03
Total cold run time: 96.52 s
Total hot run time: 24.36 s

…put of FE

### What problem does this PR solve?

Issue Number: apache#69907

Related PR: apache#69837

Problem Summary:

`escapeLabelValue` called `replace` on every label value, so a null one threw a NullPointerException, and nothing on the way to `/metrics` catches it: `DorisMetricRegistry.accept`, `MetricRepo.getMetric` and `MetricsAction` let it through, and the whole response fails. Label values can be null: a table warm-up job (`WARM UP COMPUTE GROUP <dst> WITH TABLE ...`) has no source cluster, so `CloudWarmUpJob` keeps its `srcClusterName` null, and the job registers `doris_fe_file_cache_warm_up_job_latest_start_time` each time it starts and `doris_fe_file_cache_warm_up_job_last_finish_time` when it is cancelled, both with `src_cluster_name` set to null. Neither series is ever removed, so in cloud mode the first table warm-up job to start made every scrape of the Master FE's `/metrics` fail until the FE restarted. The registry itself already accepts a null label value (`computeLabelId` maps it to an empty string). Before label values were escaped, the string concatenation wrote it as `null`.

`escapeLabelValue` now writes a null value as `null`, so the output is again unchanged for every value without a backslash, a double quote or a line feed.

### Release note

None

### Check List (For Author)

- Test: Unit Test
    - `MetricsTest#testPrometheusVisitorWritesNullLabelValue`: a gauge and a labeled histogram with a null label value are written with `null`; without this fix the case fails with a NullPointerException.
- Behavior changed: No
- Does this need documentation: No

Co-Authored-By: Claude Opus 5.5 <[email protected]>
@morningman

Copy link
Copy Markdown
Contributor Author

run buildall

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

Reviewed the complete two-file PR and traced all changed Prometheus serialization paths through MetricRepo, DorisMetricRegistry, cloud warm-up/table-stat producers, workload groups, and the public metrics endpoint. The helper correctly escapes backslashes, quotes, and line feeds while preserving the previous null rendering; the added tests cover generic labels, both histogram forms, null labels, and all four cloud table-stat series. The prior P1 null-label thread is fixed in this head and is not reposted. No new blocking or non-blocking finding was substantiated. User-provided review focus: no additional focus points were supplied. Review was static-only; builds and tests were inspected but not executed. Critical checkpoints: the change is small and stateless, introduces no new concurrency, lifecycle, configuration, persistence, or protocol path, preserves metric identity for existing values, and has no remaining coverage gap in the changed files.

Review converged after 1 round(s).

@hello-stephen

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

------ Round 1 ----------------------------------
============================================
q1	17601	5147	5061	5061
q2	2166	296	247	247
q3	10258	1414	846	846
q4	4679	458	363	363
q5	7436	819	537	537
q6	187	189	161	161
q7	779	833	613	613
q8	9260	1307	1120	1120
q9	5514	4210	4339	4210
q10	6827	1279	1032	1032
q11	441	250	230	230
q12	663	404	282	282
q13	18082	3683	3036	3036
q14	299	261	242	242
q15	q16	739	714	672	672
q17	1230	761	587	587
q18	7095	6154	6110	6110
q19	1244	955	592	592
q20	388	336	232	232
q21	6252	2501	2436	2436
q22	373	337	278	278
Total cold run time: 101513 ms
Total hot run time: 28887 ms

----- Round 2, with runtime_filter_mode=off -----
============================================
q1	5926	5906	6133	5906
q2	731	606	610	606
q3	5553	5758	5399	5399
q4	2030	2166	1560	1560
q5	5799	5827	5682	5682
q6	251	186	143	143
q7	2289	2080	1813	1813
q8	2959	2628	2750	2628
q9	7952	7525	7482	7482
q10	3641	3565	3140	3140
q11	578	420	385	385
q12	667	668	506	506
q13	3257	3638	3020	3020
q14	295	287	275	275
q15	q16	677	725	620	620
q17	7753	6994	6844	6844
q18	12929	12118	12849	12118
q19	942	785	789	785
q20	2140	2146	1921	1921
q21	5645	4620	4771	4620
q22	511	434	396	396
Total cold run time: 72525 ms
Total hot run time: 65849 ms

@hello-stephen

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

query5	4328	594	438	438
query6	426	211	190	190
query7	4804	454	226	226
query8	315	168	158	158
query9	8774	3162	3177	3162
query10	484	317	268	268
query11	6044	3880	3551	3551
query12	148	95	87	87
query13	1277	460	326	326
query14	6172	3893	3582	3582
query14_1	3474	3394	3448	3394
query15	203	197	176	176
query16	936	413	386	386
query17	845	641	512	512
query18	2406	405	294	294
query19	183	153	128	128
query20	88	82	79	79
query21	208	124	103	103
query22	12973	12993	12731	12731
query23	13418	12782	12298	12298
query23_1	12571	12382	12418	12382
query24	7369	829	468	468
query24_1	475	472	467	467
query25	514	392	333	333
query26	1255	253	138	138
query27	2779	441	278	278
query28	4647	2006	2025	2006
query29	1555	586	438	438
query30	299	218	182	182
query31	1006	922	820	820
query32	137	90	91	90
query33	525	282	232	232
query34	978	861	501	501
query35	744	757	646	646
query36	875	791	735	735
query37	126	94	84	84
query38	1774	1729	1682	1682
query39	787	769	733	733
query39_1	727	719	742	719
query40	216	117	96	96
query41	63	61	59	59
query42	86	81	86	81
query43	341	358	310	310
query44	1329	694	694	694
query45	179	174	163	163
query46	827	962	571	571
query47	2821	2951	2784	2784
query48	300	308	217	217
query49	582	405	294	294
query50	686	277	212	212
query51	10578	10434	10402	10402
query52	83	85	69	69
query53	187	214	152	152
query54	240	207	187	187
query55	73	65	61	61
query56	224	223	208	208
query57	1691	1610	1548	1548
query58	292	265	260	260
query59	2119	2154	1986	1986
query60	307	257	230	230
query61	171	164	166	164
query62	394	342	280	280
query63	193	180	163	163
query64	2860	1078	873	873
query65	3335	3283	3281	3281
query66	1801	448	313	313
query67	20000	20162	19956	19956
query68	3165	1014	619	619
query69	397	306	264	264
query70	854	836	785	785
query71	310	241	218	218
query72	2945	2726	2311	2311
query73	518	541	295	295
query74	4639	4482	4341	4341
query75	2368	2294	1931	1931
query76	2315	1009	586	586
query77	343	381	265	265
query78	9763	9767	9206	9206
query79	967	910	508	508
query80	1285	423	337	337
query81	593	318	275	275
query82	579	132	106	106
query83	289	199	180	180
query84	311	123	107	107
query85	1077	427	366	366
query86	382	242	229	229
query87	2021	1944	1795	1795
query88	3773	2777	2762	2762
query89	334	275	242	242
query90	1785	165	163	163
query91	154	151	124	124
query92	100	89	78	78
query93	1006	1010	574	574
query94	604	308	275	275
query95	578	344	317	317
query96	704	496	238	238
query97	2408	2418	2355	2355
query98	158	152	144	144
query99	776	756	653	653
Total cold run time: 235791 ms
Total hot run time: 150639 ms

@hello-stephen

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

query1	0.01	0.01	0.01
query2	0.10	0.05	0.04
query3	0.26	0.09	0.08
query4	1.60	0.10	0.11
query5	0.24	0.24	0.24
query6	1.17	0.69	0.68
query7	0.04	0.01	0.01
query8	0.06	0.03	0.04
query9	0.40	0.36	0.35
query10	0.57	0.62	0.61
query11	0.20	0.13	0.15
query12	0.19	0.14	0.15
query13	0.48	0.50	0.49
query14	1.02	1.03	1.00
query15	0.65	0.60	0.62
query16	0.35	0.37	0.36
query17	1.12	1.12	1.16
query18	0.21	0.18	0.19
query19	2.09	1.95	2.00
query20	0.02	0.01	0.02
query21	15.52	0.18	0.14
query22	5.03	0.06	0.05
query23	16.26	0.33	0.12
query24	3.07	0.49	0.44
query25	0.10	0.07	0.05
query26	1.18	0.23	0.16
query27	0.04	0.03	0.05
query28	3.57	0.78	0.36
query29	12.49	4.14	3.23
query30	0.29	0.13	0.11
query31	2.77	0.55	0.32
query32	3.22	0.61	0.50
query33	3.22	3.24	3.19
query34	15.30	4.03	3.40
query35	3.36	3.34	3.34
query36	0.56	0.49	0.42
query37	0.09	0.07	0.06
query38	0.04	0.04	0.03
query39	0.04	0.03	0.04
query40	0.20	0.15	0.14
query41	0.09	0.03	0.03
query42	0.04	0.03	0.03
query43	0.04	0.04	0.03
Total cold run time: 97.3 s
Total hot run time: 24.34 s

@hello-stephen

Copy link
Copy Markdown
Contributor

FE UT Coverage Report

Increment line coverage 94.74% (18/19) 🎉
Increment coverage report
Complete coverage report

@morningman
morningman merged commit 599616c into apache:master Nov 2, 2026
44 of 45 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.

3 participants