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 9a93ecc531 fix(consul): keep instance watch polling after a failed
query (#7433)
9a93ecc531 is described below
commit 9a93ecc5317cabdfa332a37e8f1441c24ae4b149
Author: Sean-Walker0 <[email protected]>
AuthorDate: Thu Oct 8 14:46:30 2026 +0800
fix(consul): keep instance watch polling after a failed query (#7433)
watcherStart scheduled watchConfigKeyValues directly with
scheduleWithFixedDelay and no exception guard. getHealthServices
performs the Consul blocking query and rethrows errors, and
ScheduledThreadPoolExecutor suppresses all later executions of a
periodic task once an exception escapes it, so one transient Consul
error permanently stopped the watch and stale instances were served
from watcherInstanceRegisterMap until process restart.
Catch the failure inside the scheduled task, log it, and let the next
scheduled poll retry. Regression test: the first getHealthServices
call throws, the second succeeds, and the watch must poll at least
twice (red on the previous code, where the task died after the first
failure).
Fixes #6499
Co-authored-by: Sean-Walker0
<[email protected]>
---
.../consul/ConsulInstanceRegisterRepository.java | 11 ++++++--
.../ConsulInstanceRegisterRepositoryTest.java | 30 ++++++++++++++++++++++
2 files changed, 39 insertions(+), 2 deletions(-)
diff --git
a/shenyu-registry/shenyu-registry-consul/src/main/java/org/apache/shenyu/registry/consul/ConsulInstanceRegisterRepository.java
b/shenyu-registry/shenyu-registry-consul/src/main/java/org/apache/shenyu/registry/consul/ConsulInstanceRegisterRepository.java
index af5c2785cc..b19a4d626d 100644
---
a/shenyu-registry/shenyu-registry-consul/src/main/java/org/apache/shenyu/registry/consul/ConsulInstanceRegisterRepository.java
+++
b/shenyu-registry/shenyu-registry-consul/src/main/java/org/apache/shenyu/registry/consul/ConsulInstanceRegisterRepository.java
@@ -183,8 +183,15 @@ public class ConsulInstanceRegisterRepository implements
ShenyuInstanceRegisterR
if (!watchSelectKeySet.add(selectKey)) {
return;
}
- watchFutures.add(this.executor.scheduleWithFixedDelay(() ->
this.watchConfigKeyValues(selectKey),
- 5, Integer.parseInt(watchDelay), TimeUnit.SECONDS));
+ watchFutures.add(this.executor.scheduleWithFixedDelay(() -> {
+ try {
+ this.watchConfigKeyValues(selectKey);
+ } catch (Exception e) {
+ // an exception escaping a fixed-delay task suppresses all
later executions,
+ // so a single watch failure must only be logged to keep
watching
+ LOGGER.error("consul registry watch key: {} error", selectKey,
e);
+ }
+ }, 5, Integer.parseInt(watchDelay), TimeUnit.SECONDS));
}
/**
diff --git
a/shenyu-registry/shenyu-registry-consul/src/test/java/org/apache/shenyu/registry/consul/ConsulInstanceRegisterRepositoryTest.java
b/shenyu-registry/shenyu-registry-consul/src/test/java/org/apache/shenyu/registry/consul/ConsulInstanceRegisterRepositoryTest.java
index 8d5bb3df2e..56f9d4f979 100644
---
a/shenyu-registry/shenyu-registry-consul/src/test/java/org/apache/shenyu/registry/consul/ConsulInstanceRegisterRepositoryTest.java
+++
b/shenyu-registry/shenyu-registry-consul/src/test/java/org/apache/shenyu/registry/consul/ConsulInstanceRegisterRepositoryTest.java
@@ -18,7 +18,9 @@
package org.apache.shenyu.registry.consul;
import com.ecwid.consul.v1.ConsulClient;
+import com.ecwid.consul.v1.Response;
import com.ecwid.consul.v1.agent.model.NewService;
+import com.ecwid.consul.v1.health.HealthServicesRequest;
import org.apache.shenyu.registry.api.config.RegisterConfig;
import org.apache.shenyu.registry.api.entity.InstanceEntity;
import org.apache.shenyu.registry.api.path.InstancePathConstants;
@@ -27,6 +29,7 @@ import org.junit.jupiter.api.Test;
import org.mockito.MockedConstruction;
import java.lang.reflect.Field;
+import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
@@ -35,12 +38,15 @@ import java.util.concurrent.ScheduledThreadPoolExecutor;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
import static org.junit.jupiter.api.Assertions.assertAll;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.mockConstruction;
+import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
public final class ConsulInstanceRegisterRepositoryTest {
@@ -160,4 +166,28 @@ public final class ConsulInstanceRegisterRepositoryTest {
ttlExecutor.shutdownNow();
}
}
+
+ @Test
+ public void testWatcherKeepsPollingAfterFailure() throws
NoSuchFieldException, IllegalAccessException {
+ final ConsulClient consulClient = mock(ConsulClient.class);
+ when(consulClient.getHealthServices(anyString(),
any(HealthServicesRequest.class)))
+ .thenThrow(new RuntimeException("consul temporarily
unavailable"))
+ .thenReturn(new Response<>(Collections.emptyList(), 1L,
Boolean.TRUE, 0L));
+
+ final Field consulClientField =
ConsulInstanceRegisterRepository.class.getDeclaredField("consulClient");
+ consulClientField.setAccessible(true);
+ consulClientField.set(repository, consulClient);
+ final Field watchDelayField =
ConsulInstanceRegisterRepository.class.getDeclaredField("watchDelay");
+ watchDelayField.setAccessible(true);
+ watchDelayField.set(repository, "1");
+ final Field waitTimeField =
ConsulInstanceRegisterRepository.class.getDeclaredField("waitTime");
+ waitTimeField.setAccessible(true);
+ waitTimeField.set(repository, "0");
+
+ repository.watcherStart("test-service");
+
+ verify(consulClient, timeout(15000).atLeast(2))
+ .getHealthServices(eq("test-service"),
any(HealthServicesRequest.class));
+ repository.close();
+ }
}