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 a2d9b00d4 fix(alert): bound the system-alert listing by reusing the
paginated query (#7306)
a2d9b00d4 is described below
commit a2d9b00d48a31964bdc95a73cd9faf43ecd36a8b
Author: xdz <[email protected]>
AuthorDate: Mon Oct 26 16:52:05 2026 +0800
fix(alert): bound the system-alert listing by reusing the paginated query
(#7306)
* fix(alert): bound the system-alert listing by reusing the paginated query
GET /api/system-alerts loaded every matching alert into memory and filtered
domain/instanceId/transition in Java. rmq_system_alert has no retention sweep,
so the table only grows and the unbounded read grew with it. The paginated
query already runs every filter in SQL, so the listing now reuses it with the
first page at the pagination ceiling: the response stays a plain list bounded
to the newest 100 matching alerts, and the page endpoint remains the complete
view. The unbounded repos [...]
* docs(api-spec): bound the system-alert listing at the newest 100
GET /api/system-alerts now reuses the paginated query, so its plain-list
response
carries at most the newest 100 matching alerts; the spec still read as if
every
match came back. Point the complete view at the paginated endpoint.
---
docs/api-spec.md | 3 ++
.../rocketmq/studio/ops/alert/AlertRepository.java | 2 --
.../rocketmq/studio/ops/alert/AlertService.java | 23 ++++++++------
.../ops/alert/MybatisPlusAlertRepository.java | 7 ----
.../studio/ops/alert/AlertServiceTest.java | 37 ++++++++++++++++------
.../ops/alert/MybatisPlusAlertRepositoryTest.java | 19 +++++++----
6 files changed, 55 insertions(+), 36 deletions(-)
diff --git a/docs/api-spec.md b/docs/api-spec.md
index c32bd4d23..798cb4d18 100644
--- a/docs/api-spec.md
+++ b/docs/api-spec.md
@@ -2413,6 +2413,9 @@ GET /api/system-alerts?level={level}
**Response `data`:** `SystemAlert[]`
+> 最多返回**最新 100 条**匹配记录(与分页端点同一个上限);`rmq_system_alert` 没有保留期清理,
+> 无界读取会随表一起增长,需要完整视图走分页端点 `GET /api/system-alerts/page`。
+
| 字段 | 类型 | 说明 |
|------|------|------|
| `id` | `string` | 告警 ID |
diff --git
a/server/src/main/java/org/apache/rocketmq/studio/ops/alert/AlertRepository.java
b/server/src/main/java/org/apache/rocketmq/studio/ops/alert/AlertRepository.java
index acda3fe74..79ac05ad7 100644
---
a/server/src/main/java/org/apache/rocketmq/studio/ops/alert/AlertRepository.java
+++
b/server/src/main/java/org/apache/rocketmq/studio/ops/alert/AlertRepository.java
@@ -42,8 +42,6 @@ public interface AlertRepository {
boolean deleteRule(Long id);
- List<SystemAlertVO> findAlerts(String level);
-
PageResult<SystemAlertVO> findAlerts(String level, int page, int pageSize);
Optional<SystemAlertVO> findAlertById(Long id);
diff --git
a/server/src/main/java/org/apache/rocketmq/studio/ops/alert/AlertService.java
b/server/src/main/java/org/apache/rocketmq/studio/ops/alert/AlertService.java
index 7c958018e..4ad25b59a 100644
---
a/server/src/main/java/org/apache/rocketmq/studio/ops/alert/AlertService.java
+++
b/server/src/main/java/org/apache/rocketmq/studio/ops/alert/AlertService.java
@@ -59,6 +59,9 @@ public class AlertService {
"consumer.lag.total",
SemanticMetric.CONSUMER_LAG_MESSAGES.getKey());
private static final Pattern DURATION_PATTERN = Pattern.compile(
"^" + AlertRuleRequestDTO.PROMETHEUS_DURATION_REGEXP + "$");
+ // The same ceiling validateAlertPagination enforces for the page
endpoints; the legacy
+ // listing reuses it as its result cap.
+ private static final int LIST_ALERTS_MAX = 100;
private final AlertRepository alertRepository;
private final AlertStateRepository alertStateRepository;
@@ -529,11 +532,6 @@ public class AlertService {
}
- public List<SystemAlertVO> listAlerts(String level) {
- log.info("Listing system alerts, level={}", level);
- return alertRepository.findAlerts(level);
- }
-
public PageResult<SystemAlertVO> listAlerts(String level, int page, int
pageSize) {
validateAlertPagination(page, pageSize);
String normalizedLevel = StringUtils.hasText(level) ? level.trim() :
level;
@@ -542,12 +540,17 @@ public class AlertService {
return alertRepository.findAlerts(normalizedLevel, page, pageSize);
}
+ /**
+ * The listing behind {@code GET /api/system-alerts}. It used to load
every matching alert
+ * into memory and filter domain/instanceId/transition in Java, but {@code
rmq_system_alert}
+ * has no retention sweep, so the table only grows and the unbounded read
grew with it.
+ * The paginated query already runs every filter in SQL, so the listing
reuses it with the
+ * first page at the pagination ceiling: the response shape stays a plain
list, bounded to
+ * the newest {@value #LIST_ALERTS_MAX} matching alerts, and the page
endpoint remains the
+ * complete view.
+ */
public List<SystemAlertVO> listAlerts(String level, AlertDomain domain,
String instanceId, String transition) {
- return listAlerts(level).stream()
- .filter(alert -> domain == null || domain == alert.getDomain())
- .filter(alert -> !hasText(instanceId) ||
instanceId.trim().equals(alert.getInstanceId()))
- .filter(alert -> !hasText(transition) ||
transition.trim().equalsIgnoreCase(alert.getTransition()))
- .toList();
+ return listAlerts(level, domain, instanceId, transition, 1,
LIST_ALERTS_MAX).getItems();
}
public PageResult<SystemAlertVO> listAlerts(String level, AlertDomain
domain, String instanceId,
diff --git
a/server/src/main/java/org/apache/rocketmq/studio/ops/alert/MybatisPlusAlertRepository.java
b/server/src/main/java/org/apache/rocketmq/studio/ops/alert/MybatisPlusAlertRepository.java
index 6c1cd63ba..24098c320 100644
---
a/server/src/main/java/org/apache/rocketmq/studio/ops/alert/MybatisPlusAlertRepository.java
+++
b/server/src/main/java/org/apache/rocketmq/studio/ops/alert/MybatisPlusAlertRepository.java
@@ -224,13 +224,6 @@ public class MybatisPlusAlertRepository implements
AlertRepository {
return id != null && ruleMapper.deleteById(id) > 0;
}
- @Override
- public List<SystemAlertVO> findAlerts(String level) {
- return alertMapper.selectList(alertQuery(level)).stream()
- .map(MybatisPlusAlertRepository::toAlertVO)
- .collect(Collectors.toList());
- }
-
@Override
public PageResult<SystemAlertVO> findAlerts(String level, int page, int
pageSize) {
Page<RmqSystemAlert> result = alertMapper.selectPage(
diff --git
a/server/src/test/java/org/apache/rocketmq/studio/ops/alert/AlertServiceTest.java
b/server/src/test/java/org/apache/rocketmq/studio/ops/alert/AlertServiceTest.java
index 793ca52de..c03192226 100644
---
a/server/src/test/java/org/apache/rocketmq/studio/ops/alert/AlertServiceTest.java
+++
b/server/src/test/java/org/apache/rocketmq/studio/ops/alert/AlertServiceTest.java
@@ -1379,32 +1379,49 @@ class AlertServiceTest {
}
@Test
- void listAlertsShouldReturnAlertsForLevelTest() {
+ void listAlertsShouldDelegateToTheBoundedPageQueryTest() {
SystemAlertVO alert1 =
SystemAlertVO.builder().id(1L).level(AlertLevel.error)
.title("Broker Down").acknowledged(false).build();
SystemAlertVO alert2 =
SystemAlertVO.builder().id(2L).level(AlertLevel.error)
.title("High Latency").acknowledged(false).build();
-
when(alertRepository.findAlerts("error")).thenReturn(Arrays.asList(alert1,
alert2));
+ when(alertRepository.findAlertsPage(any())).thenReturn(PageResult.of(
+ Arrays.asList(alert1, alert2), 2, 1, 100));
- List<SystemAlertVO> result = alertService.listAlerts("error");
+ List<SystemAlertVO> result = alertService.listAlerts(
+ "error", AlertDomain.CLUSTER, "local", "FIRING");
- assertThat(result).hasSize(2);
- assertThat(result.get(0).getLevel()).isEqualTo(AlertLevel.error);
- assertThat(result.get(0).getTitle()).isEqualTo("Broker Down");
- verify(alertRepository).findAlerts("error");
+ assertThat(result).containsExactly(alert1, alert2);
+ org.mockito.ArgumentCaptor<SystemAlertQuery> query =
+ org.mockito.ArgumentCaptor.forClass(SystemAlertQuery.class);
+ verify(alertRepository).findAlertsPage(query.capture());
+ assertThat(query.getValue().level()).isEqualTo("error");
+ assertThat(query.getValue().domain()).isEqualTo(AlertDomain.CLUSTER);
+ assertThat(query.getValue().instanceId()).isEqualTo("local");
+ assertThat(query.getValue().transition()).isEqualTo("FIRING");
+ // The listing is bounded by the same ceiling the paginated endpoint
enforces.
+ assertThat(query.getValue().page()).isEqualTo(1);
+ assertThat(query.getValue().pageSize()).isEqualTo(100);
}
@Test
void listAlertsShouldReturnAllAlertsWhenLevelIsNullTest() {
SystemAlertVO alert =
SystemAlertVO.builder().id(1L).level(AlertLevel.warning)
.title("Slow Consumer").build();
- when(alertRepository.findAlerts(null)).thenReturn(List.of(alert));
+ when(alertRepository.findAlertsPage(any())).thenReturn(PageResult.of(
+ List.of(alert), 1, 1, 100));
- List<SystemAlertVO> result = alertService.listAlerts(null);
+ List<SystemAlertVO> result = alertService.listAlerts(null, null, null,
null);
assertThat(result).hasSize(1);
assertThat(result.get(0).getLevel()).isEqualTo(AlertLevel.warning);
- verify(alertRepository).findAlerts(null);
+ org.mockito.ArgumentCaptor<SystemAlertQuery> query =
+ org.mockito.ArgumentCaptor.forClass(SystemAlertQuery.class);
+ verify(alertRepository).findAlertsPage(query.capture());
+ assertThat(query.getValue().level()).isNull();
+ assertThat(query.getValue().domain()).isNull();
+ assertThat(query.getValue().instanceId()).isNull();
+ assertThat(query.getValue().transition()).isNull();
+ assertThat(query.getValue().pageSize()).isEqualTo(100);
}
@Test
diff --git
a/server/src/test/java/org/apache/rocketmq/studio/ops/alert/MybatisPlusAlertRepositoryTest.java
b/server/src/test/java/org/apache/rocketmq/studio/ops/alert/MybatisPlusAlertRepositoryTest.java
index f4c5ea2cd..0ce8dc06d 100644
---
a/server/src/test/java/org/apache/rocketmq/studio/ops/alert/MybatisPlusAlertRepositoryTest.java
+++
b/server/src/test/java/org/apache/rocketmq/studio/ops/alert/MybatisPlusAlertRepositoryTest.java
@@ -218,12 +218,15 @@ class MybatisPlusAlertRepositoryTest {
}
@Test
- void findAlertsShouldNormalizeStoredLevelValues() {
+ void findAlertsPageShouldNormalizeStoredLevelValues() {
RmqSystemAlert entity = new RmqSystemAlert();
entity.setLevel(" WARNING ");
- when(alertMapper.selectList(any())).thenReturn(List.of(entity));
+ Page<RmqSystemAlert> result = new Page<>(1, 20);
+ result.setRecords(List.of(entity));
+ when(alertMapper.selectPage(any(Page.class),
any())).thenReturn(result);
- assertThat(repository.findAlerts(null)).singleElement()
+ assertThat(repository.findAlertsPage(new SystemAlertQuery(null, null,
null, null,
+ null, null, null, null, 1, 20)).getItems()).singleElement()
.satisfies(alert -> assertThat(alert.getLevel()).isEqualTo(
org.apache.rocketmq.studio.common.domain.enums.AlertLevel.warning));
}
@@ -316,18 +319,20 @@ class MybatisPlusAlertRepositoryTest {
}
@Test
- void findAlertsShouldNormalizeLevelIndependentlyOfDefaultLocaleTest() {
- when(alertMapper.selectList(any())).thenReturn(List.of());
+ void findAlertsPageShouldNormalizeLevelIndependentlyOfDefaultLocaleTest() {
+ when(alertMapper.selectPage(any(Page.class), any())).thenReturn(new
Page<RmqSystemAlert>(1, 20));
Locale originalLocale = Locale.getDefault();
try {
Locale.setDefault(Locale.forLanguageTag("tr-TR"));
- repository.findAlerts("INFO");
+ repository.findAlertsPage(new SystemAlertQuery("INFO", null, null,
null,
+ null, null, null, null, 1, 20));
} finally {
Locale.setDefault(originalLocale);
}
-
verify(alertMapper).selectList(argThat(MybatisPlusAlertRepositoryTest::hasInfoLevelParameter));
+ verify(alertMapper).selectPage(any(Page.class),
+
argThat(MybatisPlusAlertRepositoryTest::hasInfoLevelParameter));
}
@Test