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 0ab4ae6e4 fix(metrics): make two dashboard panels report what their 
titles promise (#5421)
0ab4ae6e4 is described below

commit 0ab4ae6e456f4019b49a06aba0176f495abf986e
Author: 烤化の初雪 <[email protected]>
AuthorDate: Fri Oct 9 16:25:46 2026 +0800

    fix(metrics): make two dashboard panels report what their titles promise 
(#5421)
    
    Two Grafana panels answered a different question than their titles asked.
    
    - Overview "Broker Count" used a bare 
`count(rocketmq_messages_in_total{...})`, which counts
      timeseries rather than brokers; it now counts the distinct broker label. 
(#5421)
    - DLQ "DLQ Resend Count (1m)" used `rate(...[1m])`, a per-second rate, 
under a title promising a
      one-minute count; it now uses `increase(...[1m])`. (#5422)
    
    The generator (`gen_grafana_dashboards.py`) and the committed dashboards 
are updated together.
    `test_gen_grafana_dashboards.py` 8 tests green, and the generator output 
matches the committed JSON.
    
    Folded in #5422 (same author, same generator and dashboard files).
---
 server/scripts/gen_grafana_dashboards.py           |  4 +-
 server/scripts/test_gen_grafana_dashboards.py      | 65 +++++++++++++++++++++-
 .../src/main/resources/grafana/rocketmq-dlq.json   |  2 +-
 .../main/resources/grafana/rocketmq-overview.json  |  2 +-
 4 files changed, 68 insertions(+), 5 deletions(-)

diff --git a/server/scripts/gen_grafana_dashboards.py 
b/server/scripts/gen_grafana_dashboards.py
index 775918fd5..aefc48962 100644
--- a/server/scripts/gen_grafana_dashboards.py
+++ b/server/scripts/gen_grafana_dashboards.py
@@ -173,7 +173,7 @@ specs.append((
         stat_panel(3, "Total Topics", "count(count by (topic) 
(rocketmq_messages_in_total{cluster=\"$cluster\"}))", 6, 8),
         stat_panel(4, "Total Consumer Groups", "count(count by (group) 
(rocketmq_messages_out_total{cluster=\"$cluster\"}))", 6, 8),
         stat_panel(5, "Producer Count", 
"max(rocketmq_producer_count{cluster=\"$cluster\"})", 6, 8),
-        stat_panel(6, "Broker Count", 
"count(rocketmq_messages_in_total{cluster=\"$cluster\"})", 6, 8),
+        stat_panel(6, "Broker Count", "count(count by (broker) 
(rocketmq_messages_in_total{cluster=\"$cluster\"}))", 6, 8),
     ],
 ))
 
@@ -272,7 +272,7 @@ specs.append((
     "rocketmq-dlq", "RocketMQ DLQ & Retry",
     "Dead-letter queue resend volume and latency.",
     [
-        ts_panel(1, "DLQ Resend Count (1m)", 
"rate(rocketmq_dlq_resend_count{cluster=\"$cluster\"}[1m])", 12, 0, 
"{{topic}}"),
+        ts_panel(1, "DLQ Resend Count (1m)", 
"increase(rocketmq_dlq_resend_count{cluster=\"$cluster\"}[1m])", 12, 0, 
"{{topic}}"),
         ts_panel(2, "DLQ Resend Latency", 
"rocketmq_dlq_resend_latency{cluster=\"$cluster\"}", 12, 0, "{{topic}}", "s"),
     ],
 ))
diff --git a/server/scripts/test_gen_grafana_dashboards.py 
b/server/scripts/test_gen_grafana_dashboards.py
index 17fcac5d3..bbb36b400 100644
--- a/server/scripts/test_gen_grafana_dashboards.py
+++ b/server/scripts/test_gen_grafana_dashboards.py
@@ -17,18 +17,31 @@
 
################################################################################
 """Regression tests for the Grafana dashboard asset generator."""
 
+import json
 import sys
 import unittest
 from pathlib import Path
 
 sys.path.insert(0, str(Path(__file__).resolve().parent))
-from gen_grafana_dashboards import gauge_panel, layout_panels
+from gen_grafana_dashboards import gauge_panel, layout_panels, specs
+
+DASHBOARD_DIR = Path(__file__).resolve().parent.parent / "src" / "main" / 
"resources" / "grafana"
 
 
 def panel(width, height):
     return {"gridPos": {"w": width, "h": height, "x": 99, "y": 99}}
 
 
+def shipped_panel_expr(uid, title):
+    """Expression a shipped dashboard asset renders in the panel named 
``title``."""
+    with open(DASHBOARD_DIR / f"{uid}.json", encoding="utf-8") as handle:
+        dashboard = json.load(handle)
+    for item in dashboard["panels"]:
+        if item["title"] == title:
+            return item["targets"][0]["expr"]
+    raise AssertionError(f"panel {title!r} missing from {uid}")
+
+
 class LayoutPanelsTest(unittest.TestCase):
 
     def test_packs_panels_by_width_and_advances_by_tallest_panel(self):
@@ -58,6 +71,56 @@ class LayoutPanelsTest(unittest.TestCase):
             layout_panels([panel(12, 0)])
 
 
+class OverviewPanelsTest(unittest.TestCase):
+
+    def specs_by_uid(self):
+        return {spec[0]: spec for spec in specs}
+
+    def test_broker_count_counts_brokers_not_series(self):
+        overview = self.specs_by_uid()["rocketmq-overview"]
+        panels = {item["title"]: item for item in overview[3]}
+        expr = panels["Broker Count"]["targets"][0]["expr"]
+
+        # rocketmq_messages_in_total carries both broker and topic labels - 
the same dashboard
+        # derives its $broker and $topic template variables from them - so a 
bare count() returns
+        # one series per broker/topic pair and reads hundreds on a two-broker 
cluster. The
+        # neighbouring "Total Topics" panel already counts with count(count by 
(topic) (...)).
+        self.assertEqual(
+            'count(count by (broker) 
(rocketmq_messages_in_total{cluster="$cluster"}))',
+            expr,
+        )
+
+    def test_shipped_overview_dashboard_matches_the_generator(self):
+        overview = self.specs_by_uid()["rocketmq-overview"]
+        panels = {item["title"]: item for item in overview[3]}
+
+        self.assertEqual(
+            panels["Broker Count"]["targets"][0]["expr"],
+            shipped_panel_expr("rocketmq-overview", "Broker Count"),
+        )
+
+
+class ShippedDashboardPanelsTest(unittest.TestCase):
+
+    def test_dlq_resend_count_panel_counts_over_the_window(self):
+        # The panel title and its "short" unit promise a count over the 1m 
window, but rate()
+        # renders resends per second - 60x below the number a reader takes 
from the title. The
+        # sibling "Reject Count (1m)" panel in the same bundle already uses 
increase(...[1m]).
+        self.assertEqual(
+            'increase(rocketmq_dlq_resend_count{cluster="$cluster"}[1m])',
+            shipped_panel_expr("rocketmq-dlq", "DLQ Resend Count (1m)"),
+        )
+
+    def test_shipped_dlq_dashboard_matches_the_generator(self):
+        dlq = {spec[0]: spec for spec in specs}["rocketmq-dlq"]
+        panels = {item["title"]: item for item in dlq[3]}
+
+        self.assertEqual(
+            panels["DLQ Resend Count (1m)"]["targets"][0]["expr"],
+            shipped_panel_expr("rocketmq-dlq", "DLQ Resend Count (1m)"),
+        )
+
+
 class GaugePanelTest(unittest.TestCase):
 
     def test_places_minimum_in_field_defaults(self):
diff --git a/server/src/main/resources/grafana/rocketmq-dlq.json 
b/server/src/main/resources/grafana/rocketmq-dlq.json
index c6bddd963..35d30d915 100644
--- a/server/src/main/resources/grafana/rocketmq-dlq.json
+++ b/server/src/main/resources/grafana/rocketmq-dlq.json
@@ -98,7 +98,7 @@
             "type": "prometheus",
             "uid": "${DS_PROMETHEUS}"
           },
-          "expr": "rate(rocketmq_dlq_resend_count{cluster=\"$cluster\"}[1m])",
+          "expr": 
"increase(rocketmq_dlq_resend_count{cluster=\"$cluster\"}[1m])",
           "legendFormat": "{{topic}}",
           "refId": "A"
         }
diff --git a/server/src/main/resources/grafana/rocketmq-overview.json 
b/server/src/main/resources/grafana/rocketmq-overview.json
index 2f7da454a..15119330d 100644
--- a/server/src/main/resources/grafana/rocketmq-overview.json
+++ b/server/src/main/resources/grafana/rocketmq-overview.json
@@ -313,7 +313,7 @@
             "type": "prometheus",
             "uid": "${DS_PROMETHEUS}"
           },
-          "expr": "count(rocketmq_messages_in_total{cluster=\"$cluster\"})",
+          "expr": "count(count by (broker) 
(rocketmq_messages_in_total{cluster=\"$cluster\"}))",
           "refId": "A"
         }
       ]

Reply via email to