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

Aias00 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 589e64414e [fix] keep monitor instance identity stable across edits 
(#4329)
589e64414e is described below

commit 589e64414e5f1dd9169360dc7b1e270a4a9a91e2
Author: NekoPunch <[email protected]>
AuthorDate: Mon Aug 17 21:21:50 2026 -0700

    [fix] keep monitor instance identity stable across edits (#4329)
---
 .../manager/service/impl/MonitorServiceImpl.java   | 54 ++++++++++++----------
 .../manager/service/MonitorServiceTest.java        | 53 +++++++++++++++++++++
 2 files changed, 83 insertions(+), 24 deletions(-)

diff --git 
a/hertzbeat-manager/src/main/java/org/apache/hertzbeat/manager/service/impl/MonitorServiceImpl.java
 
b/hertzbeat-manager/src/main/java/org/apache/hertzbeat/manager/service/impl/MonitorServiceImpl.java
index 63ed4fb68d..9b061731e4 100644
--- 
a/hertzbeat-manager/src/main/java/org/apache/hertzbeat/manager/service/impl/MonitorServiceImpl.java
+++ 
b/hertzbeat-manager/src/main/java/org/apache/hertzbeat/manager/service/impl/MonitorServiceImpl.java
@@ -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());
diff --git 
a/hertzbeat-manager/src/test/java/org/apache/hertzbeat/manager/service/MonitorServiceTest.java
 
b/hertzbeat-manager/src/test/java/org/apache/hertzbeat/manager/service/MonitorServiceTest.java
index 3f682f933d..6b3b5c502d 100644
--- 
a/hertzbeat-manager/src/test/java/org/apache/hertzbeat/manager/service/MonitorServiceTest.java
+++ 
b/hertzbeat-manager/src/test/java/org/apache/hertzbeat/manager/service/MonitorServiceTest.java
@@ -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()


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to