Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -127,9 +127,26 @@ public GroupAlert store(GroupAlert groupAlert) {
}
// Save alert group
groupAlert.setAlertFingerprints(alertFingerprints.stream().toList());
refreshGroupStatus(groupAlert);
GroupAlert savedGroupAlert = groupAlertDao.save(groupAlert);
savedGroupAlert.setAlerts(groupAlert.getAlerts());
return savedGroupAlert;
}
}

private void refreshGroupStatus(GroupAlert groupAlert) {
List<String> alertFingerprints = groupAlert.getAlertFingerprints();
if (alertFingerprints == null || alertFingerprints.isEmpty()) {
return;
}
List<SingleAlert> alerts = singleAlertDao.findSingleAlertsByFingerprintIn(alertFingerprints);
if (alerts == null) {
return;
}
boolean hasFiringAlert = alerts.stream()
.anyMatch(alert -> CommonConstants.ALERT_STATUS_FIRING.equals(alert.getStatus()));
groupAlert.setStatus(hasFiringAlert
? CommonConstants.ALERT_STATUS_FIRING
: CommonConstants.ALERT_STATUS_RESOLVED);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -294,13 +294,17 @@ private void sendGroupAlert(GroupAlertCache cache) {
AlertGroupConverge ruleConfig = groupDefines.get(cache.getGroupDefineName());
long repeatInterval = ruleConfig.getRepeatInterval() != null
? ruleConfig.getRepeatInterval() * MS_PER_SECOND : DEFAULT_REPEAT_INTERVAL;
boolean hasResolvedAlert = hasResolvedAlert(cache.getAlertFingerprints().values());

// Skip if within repeat interval
if (cache.getLastRepeatTime() > 0
if (!hasResolvedAlert
&& cache.getLastRepeatTime() > 0
&& now - cache.getLastRepeatTime() < repeatInterval) {
return;
}
cache.setLastRepeatTime(now);
if (!hasResolvedAlert) {
cache.setLastRepeatTime(now);
}
}

GroupAlert groupAlert = GroupAlert.builder()
Expand Down Expand Up @@ -392,6 +396,11 @@ private String determineGroupStatus(Collection<SingleAlert> alerts) {
.anyMatch(alert -> CommonConstants.ALERT_STATUS_FIRING.equals(alert.getStatus()))
? CommonConstants.ALERT_STATUS_FIRING : CommonConstants.ALERT_STATUS_RESOLVED;
}

private boolean hasResolvedAlert(Collection<SingleAlert> alerts) {
return alerts.stream()
.anyMatch(alert -> CommonConstants.ALERT_STATUS_RESOLVED.equals(alert.getStatus()));
}

@Data
private static class GroupAlertCache {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,13 @@

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyList;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import org.apache.hertzbeat.alert.dao.GroupAlertDao;
import org.apache.hertzbeat.alert.dao.SingleAlertDao;
import org.apache.hertzbeat.common.constants.CommonConstants;
import org.apache.hertzbeat.common.entity.alerter.GroupAlert;
import org.apache.hertzbeat.common.entity.alerter.SingleAlert;
import org.junit.jupiter.api.BeforeEach;
Expand Down Expand Up @@ -122,4 +124,48 @@ public void testStoreExistingAlert() {
assertEquals(1L, groupAlert.getId());
}

@Test
public void storeResolvedAlertKeepsGroupFiringWhenOtherGroupAlertsStillFire() {
String groupKey = "instance:host1";
String resolvedFingerprint = "cpu";
String firingFingerprint = "memory";
groupAlert.setGroupKey(groupKey);
groupAlert.setStatus(CommonConstants.ALERT_STATUS_RESOLVED);
singleAlert.setFingerprint(resolvedFingerprint);
singleAlert.setStatus(CommonConstants.ALERT_STATUS_RESOLVED);

GroupAlert existingGroup = new GroupAlert();
existingGroup.setId(1L);
existingGroup.setAlertFingerprints(List.of(resolvedFingerprint, firingFingerprint));
when(groupAlertDao.findByGroupKey(groupKey)).thenReturn(existingGroup);

SingleAlert previousFiringAlert = new SingleAlert();
previousFiringAlert.setId(1L);
previousFiringAlert.setFingerprint(resolvedFingerprint);
previousFiringAlert.setStatus(CommonConstants.ALERT_STATUS_FIRING);
previousFiringAlert.setStartAt(1000L);
previousFiringAlert.setActiveAt(2000L);
previousFiringAlert.setTriggerTimes(3);
when(singleAlertDao.findByFingerprint(resolvedFingerprint)).thenReturn(previousFiringAlert);

SingleAlert savedResolvedAlert = new SingleAlert();
savedResolvedAlert.setId(1L);
savedResolvedAlert.setFingerprint(resolvedFingerprint);
savedResolvedAlert.setStatus(CommonConstants.ALERT_STATUS_RESOLVED);
when(singleAlertDao.save(any(SingleAlert.class))).thenReturn(savedResolvedAlert);

SingleAlert existingFiringAlert = new SingleAlert();
existingFiringAlert.setFingerprint(firingFingerprint);
existingFiringAlert.setStatus(CommonConstants.ALERT_STATUS_FIRING);
when(singleAlertDao.findSingleAlertsByFingerprintIn(anyList()))
.thenReturn(List.of(savedResolvedAlert, existingFiringAlert));

when(groupAlertDao.save(any(GroupAlert.class))).thenAnswer(invocation -> invocation.getArgument(0));

GroupAlert savedGroupAlert = dbAlertStoreHandler.store(groupAlert);

assertEquals(CommonConstants.ALERT_STATUS_FIRING, savedGroupAlert.getStatus());
assertEquals(CommonConstants.ALERT_STATUS_FIRING, groupAlert.getStatus());
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.util.Arrays;
Expand All @@ -55,6 +56,7 @@
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import org.apache.hertzbeat.alert.dao.AlertGroupConvergeDao;
import org.apache.hertzbeat.common.constants.CommonConstants;
import org.apache.hertzbeat.common.config.VirtualThreadProperties;
import org.apache.hertzbeat.common.entity.alerter.AlertGroupConverge;
import org.apache.hertzbeat.common.entity.alerter.SingleAlert;
Expand Down Expand Up @@ -83,7 +85,7 @@ void setUp() {
when(alertGroupConvergeDao.findAlertGroupConvergesByEnableIsTrue())
.thenReturn(Collections.emptyList());
alarmGroupReduce = new AlarmGroupReduce(alarmInhibitReduce, alertGroupConvergeDao,
new VirtualThreadProperties(), false);
new VirtualThreadProperties(false, null, null, null, null, null, null), false);
}

@AfterEach
Expand Down Expand Up @@ -129,6 +131,35 @@ void whenMatchingGroupRule_shouldGroup() {
verify(alarmInhibitReduce, never()).inhibitAlarm(any()); // Should not send immediately due to group wait
}

@Test
void resolvedAlertInFiringGroupShouldBypassRepeatThrottle() {
AlertGroupConverge rule = new AlertGroupConverge();
rule.setName("test-rule");
rule.setGroupLabels(Collections.singletonList("instance"));
rule.setGroupWait(0L);
rule.setGroupInterval(0L);
rule.setRepeatInterval(60L);
alarmGroupReduce.refreshGroupDefines(Collections.singletonList(rule));

alarmGroupReduce.processGroupAlert(createAlert("cpu", CommonConstants.ALERT_STATUS_FIRING));
alarmGroupReduce.dispatchCheckAndSendGroups();
verify(alarmInhibitReduce).inhibitAlarm(argThat(group ->
CommonConstants.ALERT_STATUS_FIRING.equals(group.getStatus())
&& group.getAlerts().size() == 1
&& group.getAlerts().get(0).getFingerprint().equals("cpu")));
clearInvocations(alarmInhibitReduce);

alarmGroupReduce.processGroupAlert(createAlert("cpu", CommonConstants.ALERT_STATUS_FIRING));
alarmGroupReduce.processGroupAlert(createAlert("memory", CommonConstants.ALERT_STATUS_RESOLVED));
alarmGroupReduce.dispatchCheckAndSendGroups();

verify(alarmInhibitReduce).inhibitAlarm(argThat(group ->
CommonConstants.ALERT_STATUS_FIRING.equals(group.getStatus())
&& group.getAlerts().stream()
.anyMatch(alert -> "memory".equals(alert.getFingerprint())
&& CommonConstants.ALERT_STATUS_RESOLVED.equals(alert.getStatus()))));
}

@Test
void dispatchCheckAndSendGroupsRunsOnVirtualThread() throws Exception {
CountDownLatch latch = new CountDownLatch(1);
Expand Down Expand Up @@ -173,6 +204,15 @@ private Map<String, String> createLabels(String... keyValues) {
return labels;
}

private SingleAlert createAlert(String fingerprint, String status) {
return SingleAlert.builder()
.fingerprint(fingerprint)
.status(status)
.labels(createLabels("instance", "host1"))
.annotations(Collections.emptyMap())
.build();
}

private static final class TestAlarmGroupReduce extends AlarmGroupReduce {

private final CountDownLatch virtualThreadLatch;
Expand Down
Loading