From 3a8f75fb6ef729f6b855de3b0b1c352216a5d606 Mon Sep 17 00:00:00 2001 From: dengliming Date: Sun, 20 Sep 2026 21:21:48 +0800 Subject: [PATCH] fix(register): preserve distinct failback entries --- .../api/FailbackRegistryRepository.java | 27 +++++--- .../api/FailbackRegistryRepositoryTest.java | 62 +++++++++++++++++++ 2 files changed, 81 insertions(+), 8 deletions(-) diff --git a/shenyu-register-center/shenyu-register-client/shenyu-register-client-api/src/main/java/org/apache/shenyu/register/client/api/FailbackRegistryRepository.java b/shenyu-register-center/shenyu-register-client/shenyu-register-client-api/src/main/java/org/apache/shenyu/register/client/api/FailbackRegistryRepository.java index 47cdbb355db3..69b57f5364b3 100644 --- a/shenyu-register-center/shenyu-register-client/shenyu-register-client-api/src/main/java/org/apache/shenyu/register/client/api/FailbackRegistryRepository.java +++ b/shenyu-register-center/shenyu-register-client/shenyu-register-client-api/src/main/java/org/apache/shenyu/register/client/api/FailbackRegistryRepository.java @@ -123,7 +123,7 @@ public void persistMcpTools(final McpToolsRegisterDTO registerDTO) { protected void addFailureMetaDataRegister(final T t) { if (t instanceof MetaDataRegisterDTO) { MetaDataRegisterDTO dto = (MetaDataRegisterDTO) t; - String fullPath = dto.getRpcType() + "://" + dto.getHost() + ":" + dto.getPort() + dto.getPath(); + String fullPath = metaDataIdentity(dto); addToFail(new Holder(t, fullPath, Constants.META_TYPE)); } } @@ -137,7 +137,8 @@ protected void addFailureMetaDataRegister(final T t) { protected void addFailureUriDataRegister(final T t) { if (t instanceof URIRegisterDTO) { URIRegisterDTO dto = (URIRegisterDTO) t; - String address = String.join(":", dto.getHost(), String.valueOf(dto.getPort()), dto.getRpcType()); + String address = String.join(":", value(dto.getNamespaceId()), value(dto.getProtocol()), value(dto.getAppName()), + value(dto.getContextPath()), value(dto.getRpcType()), value(dto.getHost()), value(dto.getPort())); addToFail(new Holder(t, address, Constants.URI)); } } @@ -151,7 +152,8 @@ protected void addFailureUriDataRegister(final T t) { protected void addFailureApiDocRegister(final T t) { if (t instanceof ApiDocRegisterDTO) { ApiDocRegisterDTO dto = (ApiDocRegisterDTO) t; - String address = String.join(":", dto.getContextPath(), dto.getApiPath(), dto.getHttpMethod().toString(), dto.getRpcType()); + String address = String.join(":", value(dto.getContextPath()), value(dto.getApiPath()), value(dto.getHttpMethod()), + value(dto.getRpcType()), value(dto.getVersion())); addToFail(new Holder(t, address, Constants.API_DOC_TYPE)); } } @@ -166,19 +168,28 @@ protected void addFailureMcpDocRegister(final T t) { if (t instanceof McpToolsRegisterDTO) { McpToolsRegisterDTO dto = (McpToolsRegisterDTO) t; MetaDataRegisterDTO metaDataRegisterDTO = dto.getMetaDataRegisterDTO(); - String address = metaDataRegisterDTO.getRpcType() + "://" - + metaDataRegisterDTO.getHost() + ":" + metaDataRegisterDTO.getPort() + metaDataRegisterDTO.getPath(); + String address = String.join(":", value(dto.getNamespaceId()), metaDataIdentity(metaDataRegisterDTO)); addToFail(new Holder(dto, address, Constants.MCP_TOOLS_TYPE)); } } - private void addToFail(final Holder t) { - Holder oldObj = concurrentHashMap.get(t.getKey()); + private static String metaDataIdentity(final MetaDataRegisterDTO dto) { + return String.join(":", value(dto.getNamespaceId()), value(dto.getRpcType()), value(dto.getAppName()), + value(dto.getContextPath()), value(dto.getServiceName()), value(dto.getMethodName()), + value(dto.getParameterTypes()), value(dto.getRuleName()), value(dto.getHost()), value(dto.getPort()), value(dto.getPath())); + } + + private static String value(final Object value) { + return Objects.toString(value, ""); + } + + private void addToFail(final Holder t) { + Holder oldObj = concurrentHashMap.put(t.getKey(), t); if (Objects.nonNull(oldObj)) { + logger.warn("Updated failback registration payload, {}", t.getPath()); return; } FailureRegistryTask registryTask = new FailureRegistryTask(t.getKey(), this); - concurrentHashMap.put(t.getKey(), t); timer.add(registryTask); logger.warn("Add to failback and wait for execution, {}", t.getPath()); } diff --git a/shenyu-register-center/shenyu-register-client/shenyu-register-client-api/src/test/java/org/apache/shenyu/register/client/api/FailbackRegistryRepositoryTest.java b/shenyu-register-center/shenyu-register-client/shenyu-register-client-api/src/test/java/org/apache/shenyu/register/client/api/FailbackRegistryRepositoryTest.java index 4126ba00aed7..f4128ec487bb 100644 --- a/shenyu-register-center/shenyu-register-client/shenyu-register-client-api/src/test/java/org/apache/shenyu/register/client/api/FailbackRegistryRepositoryTest.java +++ b/shenyu-register-center/shenyu-register-client/shenyu-register-client-api/src/test/java/org/apache/shenyu/register/client/api/FailbackRegistryRepositoryTest.java @@ -30,6 +30,8 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.clearInvocations; +import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; @@ -131,6 +133,66 @@ public void testPersistMcpToolsFailure() { assertEquals(1, getFailureMapSize()); } + @Test + public void testFailuresFromDifferentNamespacesDoNotCollide() { + MetaDataRegisterDTO first = createMetaDataRegisterDTO(); + first.setNamespaceId("namespace-one"); + MetaDataRegisterDTO second = createMetaDataRegisterDTO(); + second.setNamespaceId("namespace-two"); + doThrow(new RuntimeException("Test exception")).when(repository).doPersistInterface(any()); + + repository.persistInterface(first); + repository.persistInterface(second); + + assertEquals(2, getFailureMapSize()); + } + + @Test + public void testTypeSpecificFailureIdentitiesDoNotCollide() { + URIRegisterDTO firstUri = createURIRegisterDTO(); + firstUri.setNamespaceId("namespace-one"); + URIRegisterDTO secondUri = createURIRegisterDTO(); + secondUri.setNamespaceId("namespace-two"); + ApiDocRegisterDTO firstApiDoc = createApiDocRegisterDTO(); + firstApiDoc.setVersion("v1"); + ApiDocRegisterDTO secondApiDoc = createApiDocRegisterDTO(); + secondApiDoc.setVersion("v2"); + McpToolsRegisterDTO firstMcp = createMcpToolsRegisterDTO(); + firstMcp.setNamespaceId("namespace-one"); + McpToolsRegisterDTO secondMcp = createMcpToolsRegisterDTO(); + secondMcp.setNamespaceId("namespace-two"); + doThrow(new RuntimeException("Test exception")).when(repository).doPersistURI(any()); + doThrow(new RuntimeException("Test exception")).when(repository).doPersistApiDoc(any()); + doThrow(new RuntimeException("Test exception")).when(repository).doPersistMcpTools(any()); + + repository.persistURI(firstUri); + repository.persistURI(secondUri); + repository.persistApiDoc(firstApiDoc); + repository.persistApiDoc(secondApiDoc); + repository.persistMcpTools(firstMcp); + repository.persistMcpTools(secondMcp); + + assertEquals(6, getFailureMapSize()); + } + + @Test + public void testNewerFailureReplacesStalePayload() { + MetaDataRegisterDTO stale = createMetaDataRegisterDTO(); + stale.setRpcExt("stale"); + MetaDataRegisterDTO latest = createMetaDataRegisterDTO(); + latest.setRpcExt("latest"); + doThrow(new RuntimeException("Test exception")).when(repository).doPersistInterface(any()); + + repository.persistInterface(stale); + repository.persistInterface(latest); + + assertEquals(1, getFailureMapSize()); + doNothing().when(repository).doPersistInterface(any()); + clearInvocations(repository); + repository.accept(getFirstKeyFromFailureMap()); + verify(repository).doPersistInterface(latest); + } + @Test public void testRemove() { // First add a failure