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 67596c1d22 fix(eureka): keep instance watch polling after a failed 
poll (#7429)
67596c1d22 is described below

commit 67596c1d228b62da75cd0a0ecb47d0ac5d995384
Author: Sean-Walker0 <[email protected]>
AuthorDate: Tue Oct 6 10:36:54 2026 +0800

    fix(eureka): keep instance watch polling after a failed poll (#7429)
    
    The periodic watch task in EurekaInstanceRegisterRepository caught
    polling errors, logged them, and then rethrew ShenyuException inside
    the scheduleAtFixedRate lambda. ScheduledThreadPoolExecutor suppresses
    all later executions of a periodic task once an exception escapes it,
    so one transient Eureka polling error permanently stopped the watch
    and stale instances were never updated again.
    
    Log the failure and return, letting the next scheduled poll retry.
    Regression test: the initial fetch succeeds empty, the first poll
    throws, the second poll recovers and must still deliver the ADDED
    event (red on the previous code, where the task died after the first
    failure).
    
    Fixes #6500
    
    Co-authored-by: Sean-Walker0 
<[email protected]>
---
 .../eureka/EurekaInstanceRegisterRepository.java   |  3 +-
 .../EurekaInstanceRegisterRepositoryTest.java      | 41 ++++++++++++++++++++++
 2 files changed, 43 insertions(+), 1 deletion(-)

diff --git 
a/shenyu-registry/shenyu-registry-eureka/src/main/java/org/apache/shenyu/registry/eureka/EurekaInstanceRegisterRepository.java
 
b/shenyu-registry/shenyu-registry-eureka/src/main/java/org/apache/shenyu/registry/eureka/EurekaInstanceRegisterRepository.java
index 334e7a49bb..33122bc125 100644
--- 
a/shenyu-registry/shenyu-registry-eureka/src/main/java/org/apache/shenyu/registry/eureka/EurekaInstanceRegisterRepository.java
+++ 
b/shenyu-registry/shenyu-registry-eureka/src/main/java/org/apache/shenyu/registry/eureka/EurekaInstanceRegisterRepository.java
@@ -162,8 +162,9 @@ public class EurekaInstanceRegisterRepository implements 
ShenyuInstanceRegisterR
                     compareInstances(new HashSet<>(previousInstances), new 
HashSet<>(currentInstances), listener);
                     instanceListMap.put(key, currentInstances);
                 } catch (Exception e) {
+                    // an exception escaping a fixed-rate task suppresses all 
later executions,
+                    // so a single polling failure must only be logged to keep 
the watch alive
                     LOGGER.error("eureka registry eurekaDiscoveryService watch 
key: {} error", key, e);
-                    throw new ShenyuException(e);
                 }
             }, 0, 1, TimeUnit.SECONDS);
             listenerThreadsMap.put(key, scheduledFuture);
diff --git 
a/shenyu-registry/shenyu-registry-eureka/src/test/java/org/apache/shenyu/registry/eureka/EurekaInstanceRegisterRepositoryTest.java
 
b/shenyu-registry/shenyu-registry-eureka/src/test/java/org/apache/shenyu/registry/eureka/EurekaInstanceRegisterRepositoryTest.java
index 6e963a1544..b6985af237 100644
--- 
a/shenyu-registry/shenyu-registry-eureka/src/test/java/org/apache/shenyu/registry/eureka/EurekaInstanceRegisterRepositoryTest.java
+++ 
b/shenyu-registry/shenyu-registry-eureka/src/test/java/org/apache/shenyu/registry/eureka/EurekaInstanceRegisterRepositoryTest.java
@@ -17,26 +17,37 @@
 
 package org.apache.shenyu.registry.eureka;
 
+import com.netflix.appinfo.ApplicationInfoManager;
 import com.netflix.appinfo.InstanceInfo;
 import com.netflix.discovery.DiscoveryClient;
 import com.netflix.discovery.EurekaClient;
 import com.netflix.discovery.EurekaEventListener;
 import org.apache.shenyu.registry.api.config.RegisterConfig;
 import org.apache.shenyu.registry.api.entity.InstanceEntity;
+import org.apache.shenyu.registry.api.event.ChangedEventListener;
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.mockito.MockedConstruction;
 
 import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.Map;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.nullable;
 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 EurekaInstanceRegisterRepositoryTest {
 
@@ -104,6 +115,36 @@ public final class EurekaInstanceRegisterRepositoryTest {
         assertTrue(eurekaEventStorage.isEmpty());
     }
 
+    @Test
+    public void testWatchInstancesKeepsPollingAfterFailure() throws Exception {
+        InstanceInfo info = mock(InstanceInfo.class);
+        when(info.getAppName()).thenReturn(instance.getAppName());
+        when(info.getIPAddr()).thenReturn("10.0.0.1");
+        when(info.getPort()).thenReturn(8080);
+        when(info.getInstanceId()).thenReturn("instance-1");
+        when(info.getMetadata()).thenReturn(new HashMap<>());
+        
when(info.isPortEnabled(InstanceInfo.PortType.SECURE)).thenReturn(false);
+        when(info.getStatus()).thenReturn(InstanceInfo.InstanceStatus.UP);
+
+        EurekaClient failingThenRecovering = mock(EurekaClient.class);
+        
when(failingThenRecovering.getApplicationInfoManager()).thenReturn(mock(ApplicationInfoManager.class));
+        
when(failingThenRecovering.getInstancesByVipAddressAndAppName(nullable(String.class),
 eq(instance.getAppName()), anyBoolean()))
+                .thenReturn(new ArrayList<>())
+                .thenThrow(new RuntimeException("eureka temporarily 
unavailable"))
+                .thenReturn(Collections.singletonList(info));
+
+        Field eurekaClientField = 
repository.getClass().getDeclaredField("eurekaClient");
+        eurekaClientField.setAccessible(true);
+        eurekaClientField.set(repository, failingThenRecovering);
+
+        ChangedEventListener listener = mock(ChangedEventListener.class);
+        repository.watchInstances(instance.getAppName(), listener);
+
+        verify(listener, timeout(5000).atLeastOnce())
+                .onEvent(eq(instance.getAppName()), anyString(), 
eq(ChangedEventListener.Event.ADDED));
+        repository.close();
+    }
+
     @AfterEach
     public void clear() {
         discoveryClientMockedConstruction.close();

Reply via email to