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() {