[fix] keep monitor instance identity stable across edits (#4329)

This commit is contained in:
NekoPunch
2026-08-18 12:21:50 +08:00
committed by GitHub
parent 6394a72926
commit 589e64414e
2 changed files with 83 additions and 24 deletions
@@ -138,6 +138,34 @@ public class MonitorServiceImpl implements MonitorService {
@Autowired
private MetricsFavoriteService metricsFavoriteService;
/**
* Idempotent: an instance already carrying a port is left untouched, so repeated
* edits cannot grow it (host:443:443...). A missing instance falls back to the
* host param - never concatenated onto null, which produced "null:443" identities.
*/
private void resolveMonitorInstance(Monitor monitor, List<Param> params) {
String instance = monitor.getInstance();
if (!StringUtils.hasText(instance)) {
instance = params.stream()
.filter(param -> "host".equals(param.getField()))
.map(Param::getParamValue)
.filter(StringUtils::hasText)
.findFirst()
.orElse(StringUtils.hasText(monitor.getName()) ? monitor.getName() : "unknown");
}
Param portParam = params.stream()
.filter(param -> PARAM_FIELD_PORT.equals(param.getField()))
.findFirst()
.orElse(null);
String portWithMark = (Objects.isNull(portParam) || !StringUtils.hasText(portParam.getParamValue()))
? ""
: SignConstants.DOUBLE_MARK + portParam.getParamValue();
if (!IpDomainUtil.isHasPortWithMark(instance)) {
instance = instance + portWithMark;
}
monitor.setInstance(instance);
}
@Override
@Transactional(readOnly = true)
public void detectMonitor(Monitor monitor, List<Param> params, String collector) throws MonitorDetectException {
@@ -184,19 +212,8 @@ public class MonitorServiceImpl implements MonitorService {
appDefine.setScheduleType(monitor.getScheduleType());
appDefine.setCronExpression(monitor.getCronExpression());
resolveMonitorInstance(monitor, params);
String instance = monitor.getInstance();
// The port field may be null
Param portParam = params.stream()
.filter(param -> PARAM_FIELD_PORT.equals(param.getField()))
.findFirst()
.orElse(null);
String portWithMark = (Objects.isNull(portParam) || !StringUtils.hasText(portParam.getParamValue()))
? ""
: SignConstants.DOUBLE_MARK + portParam.getParamValue();
if (!IpDomainUtil.isHasPortWithMark(instance)) {
instance = instance + portWithMark;
}
monitor.setInstance(instance);
Map<String, String> metadata = Map.of(CommonConstants.LABEL_INSTANCE_NAME, monitor.getName(),
CommonConstants.LABEL_INSTANCE, instance);
@@ -384,19 +401,8 @@ public class MonitorServiceImpl implements MonitorService {
labelDao.saveAll(addLabels);
}
resolveMonitorInstance(monitor, params);
String instance = monitor.getInstance();
// The port field may be null
Param portParam = params.stream()
.filter(param -> PARAM_FIELD_PORT.equals(param.getField()))
.findFirst()
.orElse(null);
String portWithMark = (Objects.isNull(portParam) || !StringUtils.hasText(portParam.getParamValue()))
? ""
: SignConstants.DOUBLE_MARK + portParam.getParamValue();
if (Objects.nonNull(instance)) {
instance = instance + portWithMark;
}
monitor.setInstance(instance);
boolean isStatic = CommonConstants.SCRAPE_STATIC.equals(monitor.getScrape())
|| !StringUtils.hasText(monitor.getScrape());
@@ -225,6 +225,59 @@ class MonitorServiceTest {
assertDoesNotThrow(() -> monitorService.addMonitor(monitor, params, null, null));
}
@Test
void addMonitorWithoutInstanceFallsBackToHostParam() {
Monitor monitor = Monitor.builder()
.intervals(1)
.name("memory")
.app("demoApp")
.build();
Job job = new Job();
when(appService.getAppDefine(monitor.getApp())).thenReturn(job);
when(collectJobScheduling.addAsyncCollectJob(job, null)).thenReturn(1L);
when(monitorDao.save(monitor)).thenReturn(monitor);
List<Param> params = List.of(
Param.builder().field("host").paramValue("www.example.com").build(),
Param.builder().field("port").paramValue("443").build());
when(paramDao.saveAll(params)).thenReturn(params);
assertDoesNotThrow(() -> monitorService.addMonitor(monitor, params, null, null));
assertEquals("www.example.com:443", monitor.getInstance());
}
@Test
void addMonitorInstanceStaysStableAcrossRepeatedResolution() {
Monitor monitor = Monitor.builder()
.intervals(1)
.name("memory")
.app("demoApp")
.instance("www.example.com:443")
.build();
Job job = new Job();
when(appService.getAppDefine(monitor.getApp())).thenReturn(job);
when(collectJobScheduling.addAsyncCollectJob(job, null)).thenReturn(1L);
when(monitorDao.save(monitor)).thenReturn(monitor);
List<Param> params = List.of(Param.builder().field("port").paramValue("443").build());
when(paramDao.saveAll(params)).thenReturn(params);
assertDoesNotThrow(() -> monitorService.addMonitor(monitor, params, null, null));
assertEquals("www.example.com:443", monitor.getInstance());
}
@Test
void modifyMonitorKeepsInstanceStableAcrossEdits() {
long monitorId = 7L;
Monitor stored = Monitor.builder().jobId(1L).intervals(1).app("demoApp").name("ssl")
.instance("www.example.com:443").id(monitorId).build();
when(monitorDao.findById(monitorId)).thenReturn(Optional.of(stored));
List<Param> params = List.of(Param.builder().field("port").paramValue("443").build());
for (int edit = 0; edit < 2; edit++) {
Monitor dto = Monitor.builder().jobId(1L).intervals(1).app("demoApp").name("ssl")
.instance("www.example.com:443").id(monitorId).build();
assertDoesNotThrow(() -> monitorService.modifyMonitor(dto, params, null, null));
assertEquals("www.example.com:443", dto.getInstance());
}
}
@Test
void addMonitorException() {
Monitor monitor = Monitor.builder()