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);
}