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 1f6969913e fix: make upstream lists thread-safe (#6857) (#7062)
1f6969913e is described below

commit 1f6969913e072040138bfa4f042fca4da16ef8d2
Author: Southern <[email protected]>
AuthorDate: Sat Sep 26 17:47:03 2026 +0800

    fix: make upstream lists thread-safe (#6857) (#7062)
    
    * fix: make upstream lists thread-safe (#6857)
    
      Use CopyOnWriteArrayList for healthy and unhealthy upstream collections 
so request threads can safely iterate while
      health-check threads update them.
    
      Add a regression test covering snapshot iteration and visibility of 
subsequent upstream updates.
    
    * fix: return snapshot of healthy upstream list (#6857)
    
      Return a per-request snapshot from findUpstreamListBySelectorId so 
index-based load balancers observe a stable list while
      health-check updates are in progress.
    
      Update the regression test to verify that existing snapshots remain 
unchanged and subsequent requests observe newly added
      upstreams.
    
    ---------
    
    Co-authored-by: aias00 <[email protected]>
---
 .../loadbalancer/cache/UpstreamCacheManager.java      |  4 +++-
 .../shenyu/loadbalancer/cache/UpstreamCheckTask.java  |  3 ++-
 .../loadbalancer/cache/UpstreamCacheManagerTest.java  | 19 +++++++++++++++++++
 3 files changed, 24 insertions(+), 2 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 b391242285..2c80c83fa4 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,6 +26,7 @@ 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;
@@ -125,7 +126,8 @@ public final class UpstreamCacheManager {
      * @return the list
      */
     public List<Upstream> findUpstreamListBySelectorId(final String 
selectorId) {
-        return task.getHealthyUpstream().get(selectorId);
+        List<Upstream> upstreamList = 
task.getHealthyUpstream().get(selectorId);
+        return Objects.isNull(upstreamList) ? null : new 
ArrayList<>(upstreamList);
     }
 
     /**
diff --git 
a/shenyu-loadbalancer/src/main/java/org/apache/shenyu/loadbalancer/cache/UpstreamCheckTask.java
 
b/shenyu-loadbalancer/src/main/java/org/apache/shenyu/loadbalancer/cache/UpstreamCheckTask.java
index 4a5d0ea8fb..114aff74c0 100644
--- 
a/shenyu-loadbalancer/src/main/java/org/apache/shenyu/loadbalancer/cache/UpstreamCheckTask.java
+++ 
b/shenyu-loadbalancer/src/main/java/org/apache/shenyu/loadbalancer/cache/UpstreamCheckTask.java
@@ -32,6 +32,7 @@ import java.util.List;
 import java.util.Map;
 import java.util.Objects;
 import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CopyOnWriteArrayList;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.ScheduledThreadPoolExecutor;
@@ -286,7 +287,7 @@ public final class UpstreamCheckTask implements Runnable {
      */
     public void putToMap(final Map<String, List<Upstream>> map, final String 
selectorId, final Upstream upstream) {
         synchronized (lock) {
-            List<Upstream> list = MapUtils.computeIfAbsent(map, selectorId, k 
-> Lists.newArrayList());
+            List<Upstream> list = MapUtils.computeIfAbsent(map, selectorId, k 
-> new CopyOnWriteArrayList<>());
             if (!list.contains(upstream)) {
                 list.add(upstream);
             }
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 94bfc86fd4..da6a508d21 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
@@ -80,6 +80,25 @@ public class UpstreamCacheManagerTest {
         
Assertions.assertNull(upstreamCacheManager.findUpstreamListBySelectorId(SELECTOR_ID));
     }
 
+    @Test
+    public void findUpstreamListBySelectorIdReturnsSnapshotTest() {
+        final UpstreamCacheManager upstreamCacheManager = 
UpstreamCacheManager.getInstance();
+        final String selectorId = "SNAPSHOT_TEST";
+        final Upstream upstream = 
Upstream.builder().url("snapshot-url:8080").status(true).build();
+        List<Upstream> upstreamList = new ArrayList<>(1);
+        upstreamList.add(upstream);
+        upstreamCacheManager.submit(selectorId, upstreamList);
+
+        List<Upstream> snapshot = 
upstreamCacheManager.findUpstreamListBySelectorId(selectorId);
+        Upstream added = 
Upstream.builder().url("added-url:8080").status(true).build();
+        getUpstreamCheckTask(upstreamCacheManager).triggerAddOne(selectorId, 
added);
+
+        Assertions.assertEquals(1, snapshot.size());
+        Assertions.assertSame(upstream, snapshot.get(0));
+        Assertions.assertEquals(2, 
upstreamCacheManager.findUpstreamListBySelectorId(selectorId).size());
+        upstreamCacheManager.removeByKey(selectorId);
+    }
+
     @Test
     @Order(5)
     public void testSubmitSyncsHealthCheckEnabled() {

Reply via email to