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/shenyu.git


The following commit(s) were added to refs/heads/master by this push:
     new 0de96a9dcb fix(admin): keep persisted instance state when local 
heartbeat is missing (#7697) (#7863)
0de96a9dcb is described below

commit 0de96a9dcb1d562901ab65861665baa6ec644851
Author: imDwAaY <[email protected]>
AuthorDate: Tue Oct 20 11:54:29 2026 +0800

    fix(admin): keep persisted instance state when local heartbeat is missing 
(#7697) (#7863)
    
    The instance list merged persisted rows with the node-local heartbeat cache 
and dereferenced a missing entry. Followers do not run 
InstanceCheckService.setup() and are not covered by the /instance/** forward 
list, so the whole page failed with an NPE. Keep the persisted state when the 
local entry is absent, and fall back to dateUpdated when lastHeartBeatTime has 
not been persisted yet.
---
 .../admin/controller/InstanceController.java       | 14 ++++++--
 .../admin/controller/InstanceControllerTest.java   | 39 +++++++++++++++++++++-
 2 files changed, 49 insertions(+), 4 deletions(-)

diff --git 
a/shenyu-admin/src/main/java/org/apache/shenyu/admin/controller/InstanceController.java
 
b/shenyu-admin/src/main/java/org/apache/shenyu/admin/controller/InstanceController.java
index ff515f3997..f7a3cc9ace 100644
--- 
a/shenyu-admin/src/main/java/org/apache/shenyu/admin/controller/InstanceController.java
+++ 
b/shenyu-admin/src/main/java/org/apache/shenyu/admin/controller/InstanceController.java
@@ -39,6 +39,8 @@ import org.springframework.web.bind.annotation.PostMapping;
 import org.springframework.web.bind.annotation.RequestBody;
 import org.springframework.web.bind.annotation.RequestParam;
 
+import java.util.Objects;
+
 /**
  * this is instance controller.
  */
@@ -85,9 +87,15 @@ public class InstanceController implements 
PagedController<InstanceQueryConditio
             commonPager.getDataList().forEach(instanceInfoVO -> {
                 String instanceKey = 
instanceCheckService.getInstanceKey(instanceInfoVO);
                 InstanceInfoVO instanceHealthBeatInfo = 
instanceCheckService.getInstanceHealthBeatInfo(instanceKey);
-                
instanceInfoVO.setLastHeartBeatTime(instanceHealthBeatInfo.getLastHeartBeatTime());
-                
instanceInfoVO.setInstanceState(instanceHealthBeatInfo.getInstanceState());
-                
instanceInfoVO.setDateUpdated(instanceHealthBeatInfo.getDateUpdated());
+                if (Objects.isNull(instanceHealthBeatInfo)) {
+                    if (instanceInfoVO.getLastHeartBeatTime() <= 0 && 
Objects.nonNull(instanceInfoVO.getDateUpdated())) {
+                        
instanceInfoVO.setLastHeartBeatTime(instanceInfoVO.getDateUpdated().getTime());
+                    }
+                } else {
+                    
instanceInfoVO.setLastHeartBeatTime(instanceHealthBeatInfo.getLastHeartBeatTime());
+                    
instanceInfoVO.setInstanceState(instanceHealthBeatInfo.getInstanceState());
+                    
instanceInfoVO.setDateUpdated(instanceHealthBeatInfo.getDateUpdated());
+                }
             });
         }
         return ShenyuAdminResult.success(ShenyuResultMessage.QUERY_SUCCESS, 
commonPager);
diff --git 
a/shenyu-admin/src/test/java/org/apache/shenyu/admin/controller/InstanceControllerTest.java
 
b/shenyu-admin/src/test/java/org/apache/shenyu/admin/controller/InstanceControllerTest.java
index 9b052ec3c8..72584ba51a 100644
--- 
a/shenyu-admin/src/test/java/org/apache/shenyu/admin/controller/InstanceControllerTest.java
+++ 
b/shenyu-admin/src/test/java/org/apache/shenyu/admin/controller/InstanceControllerTest.java
@@ -43,6 +43,7 @@ import java.util.ArrayList;
 import java.util.List;
 
 import static org.hamcrest.core.Is.is;
+import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.BDDMockito.given;
@@ -91,7 +92,8 @@ public final class InstanceControllerTest {
         InstanceInfoVO health = buildVO();
         health.setInstanceState(2);
         health.setLastHeartBeatTime(123L);
-        health.setDateUpdated(new Timestamp(System.currentTimeMillis()));
+        Timestamp healthDateUpdated = new Timestamp(456L);
+        health.setDateUpdated(healthDateUpdated);
         
given(this.instanceCheckService.getInstanceHealthBeatInfo(anyString())).willReturn(health);
 
         this.mockMvc.perform(MockMvcRequestBuilders.get("/instance")
@@ -106,6 +108,41 @@ public final class InstanceControllerTest {
                 .andExpect(jsonPath("$.data.dataList[0].instanceIp", 
is(vo.getInstanceIp())))
                 .andExpect(jsonPath("$.data.dataList[0].id", is("instance-1")))
                 .andExpect(jsonPath("$.data.dataList[1].id", 
is("instance-2")));
+        assertEquals(health.getLastHeartBeatTime(), vo.getLastHeartBeatTime());
+        assertEquals(health.getInstanceState(), vo.getInstanceState());
+        assertEquals(healthDateUpdated, vo.getDateUpdated());
+    }
+
+    @Test
+    public void testQueryPluginsWithMissingHealthBeatInfo() throws Exception {
+        final PageParameter pageParameter = new PageParameter(1, 10);
+        Timestamp dateUpdated = new Timestamp(123L);
+        vo.setInstanceState(2);
+        vo.setLastHeartBeatTime(0L);
+        vo.setDateUpdated(dateUpdated);
+        InstanceInfoVO persistedHeartbeatVO = buildVO();
+        persistedHeartbeatVO.setInstanceState(3);
+        persistedHeartbeatVO.setLastHeartBeatTime(456L);
+        CommonPager<InstanceInfoVO> pager = new CommonPager<>();
+        pager.setPage(pageParameter);
+        pager.setDataList(List.of(vo, persistedHeartbeatVO));
+        
given(this.instanceInfoService.listByPage(any(InstanceQuery.class))).willReturn(pager);
+        given(this.instanceCheckService.getInstanceKey((InstanceInfoVO) 
any())).willReturn("key");
+        
given(this.instanceCheckService.getInstanceHealthBeatInfo(anyString())).willReturn(null);
+
+        this.mockMvc.perform(MockMvcRequestBuilders.get("/instance")
+                        .param("instanceType", vo.getInstanceType())
+                        .param("instanceIp", vo.getInstanceIp())
+                        .param("instancePort", vo.getInstancePort())
+                        .param("namespaceId", vo.getNamespaceId())
+                        .param("currentPage", 
String.valueOf(pageParameter.getCurrentPage()))
+                        .param("pageSize", 
String.valueOf(pageParameter.getPageSize())))
+                .andExpect(status().isOk())
+                .andExpect(jsonPath("$.data.dataList[0].instanceState", is(2)))
+                .andExpect(jsonPath("$.data.dataList[0].lastHeartBeatTime", 
is(123)))
+                .andExpect(jsonPath("$.data.dataList[1].instanceState", is(3)))
+                .andExpect(jsonPath("$.data.dataList[1].lastHeartBeatTime", 
is(456)));
+        assertEquals(dateUpdated, vo.getDateUpdated());
     }
 
     @Test

Reply via email to