This is an automated email from the ASF dual-hosted git repository.

sihengyu 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 7ff5ba3164 fix: improve upstream cache management and add recovery 
test for empty events (#6294)
7ff5ba3164 is described below

commit 7ff5ba3164da052b22ebbb1ae21c8c1975a5a8a8
Author: aias00 <[email protected]>
AuthorDate: Wed Feb 25 19:07:31 2026 +0800

    fix: improve upstream cache management and add recovery test for empty 
events (#6294)
    
    * fix: improve upstream cache management and add recovery test for empty 
events
    
    * fix: streamline upstream removal process and add test for empty event 
handling
    
    ---------
    
    Co-authored-by: Yu Siheng <[email protected]>
---
 .../loadbalancer/cache/UpstreamCacheManager.java   |  9 +--
 .../shenyu/loadbalancer/entity/Upstream.java       |  2 +-
 .../cache/UpstreamCacheManagerTest.java            | 82 ++++++++++++++++++++++
 .../shenyu/loadbalancer/entity/UpstreamTest.java   |  8 +++
 4 files changed, 92 insertions(+), 9 deletions(-)

diff --git 
a/shenyu-loadbalancer/src/main/java/org/apache/shenyu/loadbalancer/cache/UpstreamCacheManager.java
 
b/shenyu-loadbalancer/src/main/java/org/apache/shenyu/loadbalancer/cache/UpstreamCacheManager.java
index 2c13303970..b391242285 100644
--- 
a/shenyu-loadbalancer/src/main/java/org/apache/shenyu/loadbalancer/cache/UpstreamCacheManager.java
+++ 
b/shenyu-loadbalancer/src/main/java/org/apache/shenyu/loadbalancer/cache/UpstreamCacheManager.java
@@ -26,7 +26,6 @@ import org.apache.shenyu.common.utils.MapUtils;
 import org.apache.shenyu.common.utils.Singleton;
 import org.apache.shenyu.loadbalancer.entity.Upstream;
 
-import java.util.ArrayList;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
@@ -150,8 +149,7 @@ public final class UpstreamCacheManager {
 
         // Check if the list is empty first to avoid unnecessary processing
         if (actualUpstreamList.isEmpty()) {
-            List<Upstream> existUpstreamList = 
MapUtils.computeIfAbsent(UPSTREAM_MAP, selectorId, k -> Lists.newArrayList());
-            removeAllUpstreams(selectorId, existUpstreamList);
+            removeByKey(selectorId);
             return;
         }
 
@@ -179,11 +177,6 @@ public final class UpstreamCacheManager {
         });
     }
 
-    private void removeAllUpstreams(final String selectorId, final 
List<Upstream> existUpstreamList) {
-        List<Upstream> toRemove = new ArrayList<>(existUpstreamList);
-        toRemove.forEach(up -> task.triggerRemoveOne(selectorId, up));
-    }
-
     private void processOfflineUpstreams(final String selectorId, final 
List<Upstream> offlineUpstreamList,
                                          final List<Upstream> 
existUpstreamList) {
         Map<String, Upstream> currentUnhealthyMap = 
getCurrentUnhealthyMap(selectorId);
diff --git 
a/shenyu-loadbalancer/src/main/java/org/apache/shenyu/loadbalancer/entity/Upstream.java
 
b/shenyu-loadbalancer/src/main/java/org/apache/shenyu/loadbalancer/entity/Upstream.java
index 36656e3493..d832dc28ff 100644
--- 
a/shenyu-loadbalancer/src/main/java/org/apache/shenyu/loadbalancer/entity/Upstream.java
+++ 
b/shenyu-loadbalancer/src/main/java/org/apache/shenyu/loadbalancer/entity/Upstream.java
@@ -509,7 +509,7 @@ public final class Upstream {
 
     @Override
     public int hashCode() {
-        return Objects.hash(protocol, url, weight);
+        return Objects.hash(protocol, url);
     }
 
     @Override
diff --git 
a/shenyu-loadbalancer/src/test/java/org/apache/shenyu/loadbalancer/cache/UpstreamCacheManagerTest.java
 
b/shenyu-loadbalancer/src/test/java/org/apache/shenyu/loadbalancer/cache/UpstreamCacheManagerTest.java
index 17b449df3c..94bfc86fd4 100644
--- 
a/shenyu-loadbalancer/src/test/java/org/apache/shenyu/loadbalancer/cache/UpstreamCacheManagerTest.java
+++ 
b/shenyu-loadbalancer/src/test/java/org/apache/shenyu/loadbalancer/cache/UpstreamCacheManagerTest.java
@@ -278,6 +278,88 @@ public class UpstreamCacheManagerTest {
         upstreamCacheManager.removeByKey(testSelectorId);
     }
 
+    @Test
+    @Order(10)
+    public void testSubmitCanRecoverAfterEmptyUpstreamEvent() {
+        final UpstreamCacheManager upstreamCacheManager = 
UpstreamCacheManager.getInstance();
+        final String testSelectorId = "RECOVER_AFTER_EMPTY_EVENT_TEST";
+
+        List<Upstream> initialList = new ArrayList<>(1);
+        initialList.add(Upstream.builder()
+                .protocol("http://";)
+                .url("recover-upstream:8080")
+                .status(true)
+                .healthCheckEnabled(false)
+                .build());
+        upstreamCacheManager.submit(testSelectorId, initialList);
+        List<Upstream> firstSubmitResult = 
upstreamCacheManager.findUpstreamListBySelectorId(testSelectorId);
+        Assertions.assertNotNull(firstSubmitResult);
+        Assertions.assertFalse(firstSubmitResult.isEmpty());
+
+        upstreamCacheManager.submit(testSelectorId, new ArrayList<>());
+        List<Upstream> afterEmptySubmitResult = 
upstreamCacheManager.findUpstreamListBySelectorId(testSelectorId);
+        Assertions.assertTrue(Objects.isNull(afterEmptySubmitResult) || 
afterEmptySubmitResult.isEmpty());
+
+        List<Upstream> recoveredList = new ArrayList<>(1);
+        recoveredList.add(Upstream.builder()
+                .protocol("http://";)
+                .url("recover-upstream:8080")
+                .status(true)
+                .healthCheckEnabled(false)
+                .build());
+        upstreamCacheManager.submit(testSelectorId, recoveredList);
+        List<Upstream> secondSubmitResult = 
upstreamCacheManager.findUpstreamListBySelectorId(testSelectorId);
+        Assertions.assertNotNull(secondSubmitResult);
+        Assertions.assertFalse(secondSubmitResult.isEmpty());
+        Assertions.assertTrue(secondSubmitResult.stream().anyMatch(upstream -> 
"recover-upstream:8080".equals(upstream.getUrl())));
+
+        upstreamCacheManager.removeByKey(testSelectorId);
+    }
+
+    @Test
+    @Order(11)
+    public void testSubmitEmptyEventClearsUnhealthyState() {
+        final UpstreamCacheManager upstreamCacheManager = 
UpstreamCacheManager.getInstance();
+        final String testSelectorId = "EMPTY_EVENT_CLEARS_UNHEALTHY_TEST";
+
+        List<Upstream> offlineList = new ArrayList<>(1);
+        offlineList.add(Upstream.builder()
+                .protocol("http://";)
+                .url("stale-upstream:8080")
+                .status(false)
+                .healthCheckEnabled(true)
+                .build());
+        upstreamCacheManager.submit(testSelectorId, offlineList);
+
+        UpstreamCheckTask task = getUpstreamCheckTask(upstreamCacheManager);
+        if (Objects.nonNull(task)) {
+            List<Upstream> unhealthyBeforeEmpty = 
task.getUnhealthyUpstream().get(testSelectorId);
+            Assertions.assertNotNull(unhealthyBeforeEmpty);
+            Assertions.assertFalse(unhealthyBeforeEmpty.isEmpty());
+        }
+
+        upstreamCacheManager.submit(testSelectorId, new ArrayList<>());
+
+        if (Objects.nonNull(task)) {
+            List<Upstream> unhealthyAfterEmpty = 
task.getUnhealthyUpstream().get(testSelectorId);
+            Assertions.assertTrue(Objects.isNull(unhealthyAfterEmpty) || 
unhealthyAfterEmpty.isEmpty());
+        }
+
+        List<Upstream> recoveredList = new ArrayList<>(1);
+        recoveredList.add(Upstream.builder()
+                .protocol("http://";)
+                .url("stale-upstream:8080")
+                .status(true)
+                .healthCheckEnabled(false)
+                .build());
+        upstreamCacheManager.submit(testSelectorId, recoveredList);
+        List<Upstream> finalResult = 
upstreamCacheManager.findUpstreamListBySelectorId(testSelectorId);
+        Assertions.assertNotNull(finalResult);
+        Assertions.assertFalse(finalResult.isEmpty());
+
+        upstreamCacheManager.removeByKey(testSelectorId);
+    }
+
     /**
      * Helper method to get the UpstreamCheckTask using reflection.
      */
diff --git 
a/shenyu-loadbalancer/src/test/java/org/apache/shenyu/loadbalancer/entity/UpstreamTest.java
 
b/shenyu-loadbalancer/src/test/java/org/apache/shenyu/loadbalancer/entity/UpstreamTest.java
index 9a9146f6b0..ab7d009707 100644
--- 
a/shenyu-loadbalancer/src/test/java/org/apache/shenyu/loadbalancer/entity/UpstreamTest.java
+++ 
b/shenyu-loadbalancer/src/test/java/org/apache/shenyu/loadbalancer/entity/UpstreamTest.java
@@ -73,7 +73,15 @@ public class UpstreamTest {
                 .weight(1)
                 .status(true)
                 .build();
+        Upstream upstream4 = Upstream.builder()
+                .protocol("https://";)
+                .url("url")
+                .weight(2)
+                .status(true)
+                .build();
         Assertions.assertEquals(upstream2, upstream3);
+        Assertions.assertEquals(upstream2, upstream4);
+        Assertions.assertEquals(upstream2.hashCode(), upstream4.hashCode());
         Assertions.assertNotNull(upstream2.toString());
         Assertions.assertTrue(upstream2.hashCode() >= 0);
     }

Reply via email to