This is an automated email from the ASF dual-hosted git repository.
lizhimins pushed a commit to branch rocketmq-studio
in repository https://gitbox.apache.org/repos/asf/rocketmq-dashboard.git
The following commit(s) were added to refs/heads/rocketmq-studio by this push:
new abdc399f7 fix(metrics): accept Prometheus decimal and exponent step
forms (#7055)
abdc399f7 is described below
commit abdc399f7e722513570794ebd764adcf99c3311b
Author: dxz0069 <[email protected]>
AuthorDate: Mon Oct 26 16:28:56 2026 +0800
fix(metrics): accept Prometheus decimal and exponent step forms (#7055)
The step syntax gate required digits on both sides of a decimal point
and excluded scientific notation, so .5, 1., +1, 1e1, 5E-1 and 1e0 were
rejected as invalid before dispatch even though Prometheus 3.5.0 and the
Studio PrometheusMetricsSource accept them. The gate now matches the
Prometheus float grammar - sign, decimal point on either side, exponent
- which BigDecimal's own grammar accepts, so the step text parses and is
dispatched unchanged. Positivity and the 11,000-point sample budget are
unchanged downstream checks on the parsed value: a 20-second range still
rejects 0.001, .001 and 1e-3.
Fixes #6948
---
.../studio/cluster/metrics/MetricsService.java | 9 ++++-
.../studio/cluster/metrics/MetricsServiceTest.java | 39 ++++++++++++++++++++++
2 files changed, 47 insertions(+), 1 deletion(-)
diff --git
a/server/src/main/java/org/apache/rocketmq/studio/cluster/metrics/MetricsService.java
b/server/src/main/java/org/apache/rocketmq/studio/cluster/metrics/MetricsService.java
index c1c9bf26c..c6b83242d 100644
---
a/server/src/main/java/org/apache/rocketmq/studio/cluster/metrics/MetricsService.java
+++
b/server/src/main/java/org/apache/rocketmq/studio/cluster/metrics/MetricsService.java
@@ -39,7 +39,14 @@ import java.util.regex.Pattern;
public class MetricsService {
private static final long MAX_RANGE_SECONDS = 31L * 24 * 60 * 60;
private static final long MAX_SAMPLE_POINTS = 11_000L;
- private static final Pattern NUMBER_PATTERN =
Pattern.compile("\\d+(?:\\.\\d+)?");
+ /**
+ * Prometheus accepts a float literal for step — sign, a decimal point on
either side of the
+ * digits, and an exponent — and {@link BigDecimal}'s grammar accepts
exactly that, so the
+ * syntax gate stays aligned with the backends that receive the step
unchanged. Positivity
+ * and the sample budget are enforced downstream on the parsed value.
+ */
+ private static final Pattern NUMBER_PATTERN =
+
Pattern.compile("[+-]?(?:\\d+(?:\\.\\d*)?|\\.\\d+)(?:[eE][+-]?\\d+)?");
private static final Pattern DURATION_PART_PATTERN =
Pattern.compile("(\\d+)(ms|s|m|h|d|w|y)");
private static final Map<String, BigDecimal> UNIT_TO_MILLIS = Map.of(
"ms", BigDecimal.ONE,
diff --git
a/server/src/test/java/org/apache/rocketmq/studio/cluster/metrics/MetricsServiceTest.java
b/server/src/test/java/org/apache/rocketmq/studio/cluster/metrics/MetricsServiceTest.java
index 75cf69c1b..dae711a06 100644
---
a/server/src/test/java/org/apache/rocketmq/studio/cluster/metrics/MetricsServiceTest.java
+++
b/server/src/test/java/org/apache/rocketmq/studio/cluster/metrics/MetricsServiceTest.java
@@ -195,6 +195,45 @@ class MetricsServiceTest {
verify(metricsSource).query(queryNumeric);
}
+ @Test
+ void queryShouldAcceptPrometheusDecimalAndExponentStepFormsTest() {
+ // Prometheus accepts a float literal for step — a sign, a decimal
point on either side
+ // of the digits, an exponent — and the source receives the step text
unchanged, so the
+ // service must not reject equivalent numeric forms a real backend
would take.
+ MetricDataVO data = emptyMetricData();
+ when(metricsSource.query(any(MetricQueryDTO.class))).thenReturn(data);
+ for (String step : List.of(".5", "1.", "+1", "1e1", "5E-1", "1e0")) {
+ MetricQueryDTO query = MetricQueryDTO.builder()
+ .metric("vector(1)")
+ .start(1_700_000_000L)
+ .end(1_700_000_020L)
+ .step(step)
+ .build();
+
+ MetricDataVO result = metricsService.query(query);
+
+ assertThat(result).isNotNull();
+ verify(metricsSource).query(query);
+ }
+ }
+
+ @Test
+ void queryShouldStillRejectSubBudgetStepsInExponentNotationTest() {
+ // Widened syntax must not widen semantics: a 20-second range with a
millisecond step
+ // stays over the sample budget whatever notation names it.
+ for (String step : List.of("0.001", ".001", "1e-3")) {
+ MetricQueryDTO query = MetricQueryDTO.builder()
+ .metric("vector(1)")
+ .start(1_700_000_000L)
+ .end(1_700_000_020L)
+ .step(step)
+ .build();
+
+ assertBadRequest(query, "Metric query returns too many samples;
increase step or reduce range");
+ }
+ verifyNoInteractions(metricsSource);
+ }
+
@Test
void queryShouldReturnMultipleDataPoints() {
MetricQueryDTO query = MetricQueryDTO.builder()