This is an automated email from the ASF dual-hosted git repository. shown pushed a commit to branch 0321-yuluo/fix in repository https://gitbox.apache.org/repos/asf/hertzbeat.git
commit f6097489e104a28ed2e0b80d5c24e3dc94810824 Author: yuluo-yx <[email protected]> AuthorDate: Sat Mar 21 18:05:59 2026 +0800 [fix] fix cyclic silence time matching and cross-midnight handling --- .../hertzbeat/alert/reduce/AlarmSilenceReduce.java | 34 +++++++-- .../alert/reduce/AlarmSilenceReduceTest.java | 82 ++++++++++++++++++++++ 2 files changed, 112 insertions(+), 4 deletions(-) diff --git a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/reduce/AlarmSilenceReduce.java b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/reduce/AlarmSilenceReduce.java index 4fdf27746b..7dfad6360d 100644 --- a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/reduce/AlarmSilenceReduce.java +++ b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/reduce/AlarmSilenceReduce.java @@ -18,6 +18,7 @@ package org.apache.hertzbeat.alert.reduce; import java.time.LocalDateTime; +import java.time.LocalTime; import java.util.List; import java.util.Map; import java.util.Optional; @@ -95,10 +96,35 @@ public class AlarmSilenceReduce { * @return true if alert should not be silenced, false if alert should be silenced */ private boolean checkAndSave(LocalDateTime now, AlertSilence alertSilence) { - boolean startMatch = alertSilence.getPeriodStart() == null - || now.isAfter(alertSilence.getPeriodStart().toLocalDateTime()); - boolean endMatch = alertSilence.getPeriodEnd() == null - || now.isBefore(alertSilence.getPeriodEnd().toLocalDateTime()); + boolean startMatch; + boolean endMatch; + if (alertSilence.getType() == 1) { + LocalTime nowTime = now.toLocalTime(); + LocalTime startTime = alertSilence.getPeriodStart() == null ? null : alertSilence.getPeriodStart().toLocalTime(); + LocalTime endTime = alertSilence.getPeriodEnd() == null ? null : alertSilence.getPeriodEnd().toLocalTime(); + if (startTime == null && endTime == null) { + startMatch = true; + endMatch = true; + } else if (startTime == null) { + startMatch = true; + endMatch = !nowTime.isAfter(endTime); + } else if (endTime == null) { + startMatch = !nowTime.isBefore(startTime); + endMatch = true; + } else if (!startTime.isAfter(endTime)) { + startMatch = !nowTime.isBefore(startTime); + endMatch = !nowTime.isAfter(endTime); + } else { + // Cross-midnight window, e.g. 23:00-02:00. + startMatch = !nowTime.isBefore(startTime) || !nowTime.isAfter(endTime); + endMatch = true; + } + } else { + startMatch = alertSilence.getPeriodStart() == null + || now.isAfter(alertSilence.getPeriodStart().toLocalDateTime()); + endMatch = alertSilence.getPeriodEnd() == null + || now.isBefore(alertSilence.getPeriodEnd().toLocalDateTime()); + } if (startMatch && endMatch) { int time = Optional.ofNullable(alertSilence.getTimes()).orElse(0); diff --git a/hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/reduce/AlarmSilenceReduceTest.java b/hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/reduce/AlarmSilenceReduceTest.java index b9bc9ea9df..2c9d7d0e42 100644 --- a/hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/reduce/AlarmSilenceReduceTest.java +++ b/hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/reduce/AlarmSilenceReduceTest.java @@ -119,6 +119,88 @@ class AlarmSilenceReduceTest { verify(alertSilenceDao).save(silenceRule); } + @Test + void whenCyclicSilenceRuleTimeMatchesButDateDifferent_shouldNotForwardAlert() { + LocalDateTime now = LocalDateTime.now(); + LocalDateTime yesterday = now.minusDays(1); + LocalDateTime start = yesterday.withHour(now.getHour()) + .withMinute(Math.max(0, now.getMinute() - 10)) + .withSecond(0) + .withNano(0); + LocalDateTime end = yesterday.withHour(now.getHour()) + .withMinute(Math.min(59, now.getMinute() + 10)) + .withSecond(59) + .withNano(0); + + AlertSilence silenceRule = AlertSilence.builder() + .enable(true) + .matchAll(false) + .type((byte) 1) + .labels(createLabels("service", "web")) + .periodStart(start.atZone(ZoneId.systemDefault())) + .periodEnd(end.atZone(ZoneId.systemDefault())) + .days(Collections.singletonList((byte) now.getDayOfWeek().getValue())) + .times(0) + .build(); + + when(alertSilenceDao.findAlertSilencesByEnableTrue()).thenReturn(Collections.singletonList(silenceRule)); + when(alertSilenceDao.save(any(AlertSilence.class))).thenReturn(silenceRule); + + GroupAlert alert = createGroupAlert("firing", createLabels("service", "web")); + alarmSilenceReduce.silenceAlarm(alert); + + verify(alertNoticeDispatch, never()).dispatchAlarm(alert); + verify(alertSilenceDao).save(silenceRule); + } + + @Test + void whenMatchingCrossMidnightCyclicSilenceRule_shouldNotForwardAlert() { + LocalDateTime now = LocalDateTime.now(); + AlertSilence silenceRule = AlertSilence.builder() + .enable(true) + .matchAll(false) + .type((byte) 1) + .labels(createLabels("service", "web")) + .periodStart(now.minusHours(1).atZone(ZoneId.systemDefault())) + .periodEnd(now.minusHours(2).atZone(ZoneId.systemDefault())) + .days(Collections.singletonList((byte) now.getDayOfWeek().getValue())) + .times(0) + .build(); + + when(alertSilenceDao.findAlertSilencesByEnableTrue()).thenReturn(Collections.singletonList(silenceRule)); + when(alertSilenceDao.save(any(AlertSilence.class))).thenReturn(silenceRule); + + GroupAlert alert = createGroupAlert("firing", createLabels("service", "web")); + alarmSilenceReduce.silenceAlarm(alert); + + verify(alertNoticeDispatch, never()).dispatchAlarm(alert); + verify(alertSilenceDao).save(silenceRule); + } + + @Test + void whenCrossMidnightCyclicSilenceRuleDoesNotIncludeCurrentDay_shouldForwardAlert() { + LocalDateTime now = LocalDateTime.now(); + byte previousDay = (byte) now.minusDays(1).getDayOfWeek().getValue(); + AlertSilence silenceRule = AlertSilence.builder() + .enable(true) + .matchAll(false) + .type((byte) 1) + .labels(createLabels("service", "web")) + .periodStart(now.minusHours(1).atZone(ZoneId.systemDefault())) + .periodEnd(now.minusHours(2).atZone(ZoneId.systemDefault())) + .days(Collections.singletonList(previousDay)) + .times(0) + .build(); + + when(alertSilenceDao.findAlertSilencesByEnableTrue()).thenReturn(Collections.singletonList(silenceRule)); + + GroupAlert alert = createGroupAlert("firing", createLabels("service", "web")); + alarmSilenceReduce.silenceAlarm(alert); + + verify(alertNoticeDispatch).dispatchAlarm(alert); + verify(alertSilenceDao, never()).save(any()); + } + @Test void whenSilenceRuleExpired_shouldForwardAlert() { AlertSilence silenceRule = AlertSilence.builder() --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
