This is an automated email from the ASF dual-hosted git repository.

gongchao pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/hertzbeat.git


The following commit(s) were added to refs/heads/master by this push:
     new a4d2630320 [fix] fix cyclic silence time matching and cross-midnight 
handling (#4081)
a4d2630320 is described below

commit a4d2630320734e952e2abab0b5be16e4e4088230
Author: shown <[email protected]>
AuthorDate: Sun Mar 22 22:05:27 2026 +0800

    [fix] fix cyclic silence time matching and cross-midnight handling (#4081)
    
    Co-authored-by: yunfan24 <[email protected]>
    Co-authored-by: Tomsun28 <[email protected]>
---
 .../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]

Reply via email to