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()

Reply via email to