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