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 eb7428279f fix(admin): persist upstream status changes into selector 
handle (#6528)
eb7428279f is described below

commit eb7428279f4d44fa9076073157686a21d0a74100
Author: Duansg <[email protected]>
AuthorDate: Wed Sep 30 09:28:23 2026 +0800

    fix(admin): persist upstream status changes into selector handle (#6528)
    
    * fix(admin): persist upstream status changes into selector handle
    
    * supplementary repair
    
    ---------
    
    Co-authored-by: aias00 <[email protected]>
---
 .../AbstractShenyuClientRegisterServiceImpl.java   | 18 +++++++
 .../ShenyuClientRegisterDivideServiceImpl.java     |  1 +
 .../ShenyuClientRegisterDubboServiceImpl.java      |  1 +
 .../ShenyuClientRegisterGrpcServiceImpl.java       |  1 +
 .../ShenyuClientRegisterTarsServiceImpl.java       |  1 +
 .../ShenyuClientRegisterWebSocketServiceImpl.java  | 13 +++--
 .../ShenyuClientRegisterDivideServiceImplTest.java | 54 +++++++++++++++++++++
 .../ShenyuClientRegisterDubboServiceImplTest.java  | 56 ++++++++++++++++++++++
 .../ShenyuClientRegisterGrpcServiceImplTest.java   | 55 +++++++++++++++++++++
 .../ShenyuClientRegisterTarsServiceImplTest.java   | 54 +++++++++++++++++++++
 ...enyuClientRegisterWebSocketServiceImplTest.java | 31 ++++++++++++
 11 files changed, 280 insertions(+), 5 deletions(-)

diff --git 
a/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/register/AbstractShenyuClientRegisterServiceImpl.java
 
b/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/register/AbstractShenyuClientRegisterServiceImpl.java
index 167383a12c..c4b95a939b 100644
--- 
a/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/register/AbstractShenyuClientRegisterServiceImpl.java
+++ 
b/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/register/AbstractShenyuClientRegisterServiceImpl.java
@@ -363,6 +363,24 @@ public abstract class 
AbstractShenyuClientRegisterServiceImpl extends FallbackSh
                 .toList().stream().findAny().orElse(false);
     }
 
+    /**
+     * Sync the changed status back to the exist list, so that the status 
change
+     * can be persisted into the selector handle.
+     *
+     * <p>The upstream {@code equals} methods only compare the identity 
fields, so an upstream whose
+     * status just changed is still regarded as an existing one and keeps its 
old status in the exist list.
+     *
+     * @param existList      the upstream list parsed from the selector handle
+     * @param diffStatusList the upstream list whose status has changed
+     * @param <T>            the upstream type
+     */
+    protected <T extends CommonUpstream> void syncUpstreamStatus(final List<T> 
existList, final List<T> diffStatusList) {
+        for (T changed : diffStatusList) {
+            existList.stream().filter(exist -> exist.equals(changed))
+                    .forEach(exist -> exist.setStatus(changed.isStatus()));
+        }
+    }
+
     /**
      * Build context path default rule dto rule dto.
      *
diff --git 
a/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/register/ShenyuClientRegisterDivideServiceImpl.java
 
b/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/register/ShenyuClientRegisterDivideServiceImpl.java
index b83de054b7..956a592027 100644
--- 
a/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/register/ShenyuClientRegisterDivideServiceImpl.java
+++ 
b/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/register/ShenyuClientRegisterDivideServiceImpl.java
@@ -92,6 +92,7 @@ public class ShenyuClientRegisterDivideServiceImpl extends 
AbstractContextPathRe
                     || existList.stream().anyMatch(e -> e.equals(upstream) && 
e.isStatus() != upstream.isStatus())).collect(Collectors.toList());
             if (CollectionUtils.isNotEmpty(diffStatusList)) {
                 canAddList.addAll(diffStatusList);
+                syncUpstreamStatus(existList, diffStatusList);
             }
         }
 
diff --git 
a/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/register/ShenyuClientRegisterDubboServiceImpl.java
 
b/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/register/ShenyuClientRegisterDubboServiceImpl.java
index 86eefa9a32..51d84e8f8f 100644
--- 
a/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/register/ShenyuClientRegisterDubboServiceImpl.java
+++ 
b/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/register/ShenyuClientRegisterDubboServiceImpl.java
@@ -84,6 +84,7 @@ public class ShenyuClientRegisterDubboServiceImpl extends 
AbstractShenyuClientRe
                     || existList.stream().anyMatch(e -> e.equals(upstream) && 
e.isStatus() != upstream.isStatus())).collect(Collectors.toList());
             if (CollectionUtils.isNotEmpty(diffStatusList)) {
                 canAddList.addAll(diffStatusList);
+                syncUpstreamStatus(existList, diffStatusList);
             }
         }
 
diff --git 
a/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/register/ShenyuClientRegisterGrpcServiceImpl.java
 
b/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/register/ShenyuClientRegisterGrpcServiceImpl.java
index d6983bcd13..0088e2a19b 100644
--- 
a/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/register/ShenyuClientRegisterGrpcServiceImpl.java
+++ 
b/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/register/ShenyuClientRegisterGrpcServiceImpl.java
@@ -107,6 +107,7 @@ public class ShenyuClientRegisterGrpcServiceImpl extends 
AbstractShenyuClientReg
                     || existList.stream().anyMatch(e -> e.equals(upstream) && 
e.isStatus() != upstream.isStatus())).collect(Collectors.toList());
             if (CollectionUtils.isNotEmpty(diffStatusList)) {
                 canAddList.addAll(diffStatusList);
+                syncUpstreamStatus(existList, diffStatusList);
             }
         }
         if (doSubmit(selectorDO.getId(), canAddList)) {
diff --git 
a/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/register/ShenyuClientRegisterTarsServiceImpl.java
 
b/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/register/ShenyuClientRegisterTarsServiceImpl.java
index 2f6fd65b66..7fe50a1a02 100644
--- 
a/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/register/ShenyuClientRegisterTarsServiceImpl.java
+++ 
b/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/register/ShenyuClientRegisterTarsServiceImpl.java
@@ -88,6 +88,7 @@ public class ShenyuClientRegisterTarsServiceImpl extends 
AbstractShenyuClientReg
                     || existList.stream().anyMatch(e -> e.equals(upstream) && 
e.isStatus() != upstream.isStatus())).collect(Collectors.toList());
             if (CollectionUtils.isNotEmpty(diffStatusList)) {
                 canAddList.addAll(diffStatusList);
+                syncUpstreamStatus(existList, diffStatusList);
             }
         }
 
diff --git 
a/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/register/ShenyuClientRegisterWebSocketServiceImpl.java
 
b/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/register/ShenyuClientRegisterWebSocketServiceImpl.java
index 7787bc73a5..7fe74ff867 100644
--- 
a/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/register/ShenyuClientRegisterWebSocketServiceImpl.java
+++ 
b/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/register/ShenyuClientRegisterWebSocketServiceImpl.java
@@ -76,23 +76,26 @@ public class ShenyuClientRegisterWebSocketServiceImpl 
extends AbstractContextPat
 
     @Override
     protected String buildHandle(final List<URIRegisterDTO> uriList, final 
SelectorDO selectorDO) {
-        String handleAdd;
         List<WebSocketUpstream> addList = buildWebSocketUpstreamList(uriList);
         List<WebSocketUpstream> canAddList = new CopyOnWriteArrayList<>();
         List<WebSocketUpstream> existList = 
GsonUtils.getInstance().fromCurrentList(selectorDO.getHandle(), 
WebSocketUpstream.class);
         if (CollectionUtils.isEmpty(existList)) {
-            handleAdd = GsonUtils.getInstance().toJson(addList);
             canAddList = addList;
         } else {
-            List<WebSocketUpstream> diffList = 
addList.stream().filter(divideUpstream -> 
!existList.contains(divideUpstream)).collect(Collectors.toList());
+            List<WebSocketUpstream> diffList = 
addList.stream().filter(upstream -> 
!existList.contains(upstream)).collect(Collectors.toList());
             if (CollectionUtils.isNotEmpty(diffList)) {
                 canAddList.addAll(diffList);
                 existList.addAll(diffList);
             }
-            handleAdd = GsonUtils.getInstance().toJson(existList);
+            List<WebSocketUpstream> diffStatusList = 
addList.stream().filter(upstream -> !upstream.isStatus()
+                    || existList.stream().anyMatch(e -> e.equals(upstream) && 
e.isStatus() != upstream.isStatus())).collect(Collectors.toList());
+            if (CollectionUtils.isNotEmpty(diffStatusList)) {
+                canAddList.addAll(diffStatusList);
+                syncUpstreamStatus(existList, diffStatusList);
+            }
         }
         doSubmit(selectorDO.getId(), canAddList);
-        return handleAdd;
+        return 
GsonUtils.getInstance().toJson(CollectionUtils.isEmpty(existList) ? canAddList 
: existList);
     }
 
     private List<WebSocketUpstream> buildWebSocketUpstreamList(final 
List<URIRegisterDTO> uriList) {
diff --git 
a/shenyu-admin/src/test/java/org/apache/shenyu/admin/service/register/ShenyuClientRegisterDivideServiceImplTest.java
 
b/shenyu-admin/src/test/java/org/apache/shenyu/admin/service/register/ShenyuClientRegisterDivideServiceImplTest.java
index cda0c97b44..efcd909407 100644
--- 
a/shenyu-admin/src/test/java/org/apache/shenyu/admin/service/register/ShenyuClientRegisterDivideServiceImplTest.java
+++ 
b/shenyu-admin/src/test/java/org/apache/shenyu/admin/service/register/ShenyuClientRegisterDivideServiceImplTest.java
@@ -29,6 +29,7 @@ import org.apache.shenyu.common.exception.ShenyuException;
 import org.apache.shenyu.common.utils.GsonUtils;
 import org.apache.shenyu.register.common.dto.MetaDataRegisterDTO;
 import org.apache.shenyu.register.common.dto.URIRegisterDTO;
+import org.apache.shenyu.register.common.enums.EventType;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.ExtendWith;
@@ -43,6 +44,8 @@ import java.util.ArrayList;
 import java.util.List;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
@@ -132,6 +135,57 @@ public final class 
ShenyuClientRegisterDivideServiceImplTest {
         assertEquals(resultList.size(), 1);
     }
 
+    /**
+     * The status change of an existing upstream should be written back into 
the selector handle.
+     * see <a href="https://github.com/apache/shenyu/issues/6522";>issue 
#6522</a>
+     */
+    @Test
+    public void testBuildHandleWithStatusChanged() {
+        shenyuClientRegisterDivideService = 
spy(shenyuClientRegisterDivideService);
+
+        final String returnStr = 
"[{protocol:'http://',upstreamHost:'localhost',upstreamUrl:'localhost:8090',warmup:10,weight:50,status:true,timestamp:1637826588267,\"gray\":false},"
+                + 
"{protocol:'http://',upstreamHost:'localhost',upstreamUrl:'localhost:8091',warmup:10,weight:50,status:true,timestamp:1637826588267,\"gray\":false}]";
+
+        List<URIRegisterDTO> list = new ArrayList<>();
+        
list.add(URIRegisterDTO.builder().protocol("http://";).appName("test1").rpcType(RpcTypeEnum.HTTP.getName())
+                
.host(LOCALHOST).port(8090).eventType(EventType.DELETED).build());
+        SelectorDO selectorDO = mock(SelectorDO.class);
+        when(selectorDO.getHandle()).thenReturn(returnStr);
+        
doReturn(false).when(shenyuClientRegisterDivideService).doSubmit(any(), any());
+        String actual = shenyuClientRegisterDivideService.buildHandle(list, 
selectorDO);
+
+        List<DivideUpstream> resultList = 
GsonUtils.getInstance().fromCurrentList(actual, DivideUpstream.class);
+        assertEquals(2, resultList.size());
+        assertFalse(resultList.get(0).isStatus());
+        assertTrue(resultList.get(1).isStatus());
+    }
+
+    /**
+     * An offline upstream coming back online should be written back into the 
selector handle as well,
+     * and only the re-registered one should be touched.
+     * see <a href="https://github.com/apache/shenyu/issues/6522";>issue 
#6522</a>
+     */
+    @Test
+    public void testBuildHandleWithStatusRecovered() {
+        shenyuClientRegisterDivideService = 
spy(shenyuClientRegisterDivideService);
+
+        final String returnStr = 
"[{protocol:'http://',upstreamHost:'localhost',upstreamUrl:'localhost:8090',warmup:10,weight:50,status:false,timestamp:1637826588267,\"gray\":false},"
+                + 
"{protocol:'http://',upstreamHost:'localhost',upstreamUrl:'localhost:8091',warmup:10,weight:50,status:false,timestamp:1637826588267,\"gray\":false}]";
+
+        List<URIRegisterDTO> list = new ArrayList<>();
+        
list.add(URIRegisterDTO.builder().protocol("http://";).appName("test1").rpcType(RpcTypeEnum.HTTP.getName())
+                .host(LOCALHOST).port(8090).build());
+        SelectorDO selectorDO = mock(SelectorDO.class);
+        when(selectorDO.getHandle()).thenReturn(returnStr);
+        
doReturn(false).when(shenyuClientRegisterDivideService).doSubmit(any(), any());
+        String actual = shenyuClientRegisterDivideService.buildHandle(list, 
selectorDO);
+
+        List<DivideUpstream> resultList = 
GsonUtils.getInstance().fromCurrentList(actual, DivideUpstream.class);
+        assertEquals(2, resultList.size());
+        assertTrue(resultList.get(0).isStatus());
+        assertFalse(resultList.get(1).isStatus());
+    }
+
     @Test
     public void testBuildDivideUpstreamList() {
         List<URIRegisterDTO> list = new ArrayList<>();
diff --git 
a/shenyu-admin/src/test/java/org/apache/shenyu/admin/service/register/ShenyuClientRegisterDubboServiceImplTest.java
 
b/shenyu-admin/src/test/java/org/apache/shenyu/admin/service/register/ShenyuClientRegisterDubboServiceImplTest.java
index d81f2c973b..00445e26ad 100644
--- 
a/shenyu-admin/src/test/java/org/apache/shenyu/admin/service/register/ShenyuClientRegisterDubboServiceImplTest.java
+++ 
b/shenyu-admin/src/test/java/org/apache/shenyu/admin/service/register/ShenyuClientRegisterDubboServiceImplTest.java
@@ -29,6 +29,7 @@ import org.apache.shenyu.common.exception.ShenyuException;
 import org.apache.shenyu.common.utils.GsonUtils;
 import org.apache.shenyu.register.common.dto.MetaDataRegisterDTO;
 import org.apache.shenyu.register.common.dto.URIRegisterDTO;
+import org.apache.shenyu.register.common.enums.EventType;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.ExtendWith;
 import org.mockito.InjectMocks;
@@ -42,6 +43,8 @@ import java.util.ArrayList;
 import java.util.List;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
@@ -135,6 +138,59 @@ public final class 
ShenyuClientRegisterDubboServiceImplTest {
         assertEquals(resultList.size(), 1);
     }
 
+    /**
+     * The status change of an existing upstream should be written back into 
the selector handle.
+     * see <a href="https://github.com/apache/shenyu/issues/6522";>issue 
#6522</a>
+     */
+    @Test
+    public void testBuildHandleWithStatusChanged() {
+        shenyuClientRegisterDubboService = 
spy(shenyuClientRegisterDubboService);
+
+        final String returnStr = 
"[{protocol:'dubbo://',upstreamHost:'localhost',upstreamUrl:'localhost:8090',warmup:10,weight:50,status:true,timestamp:1637826588267,\"gray\":false},"
+                + 
"{protocol:'dubbo://',upstreamHost:'localhost',upstreamUrl:'localhost:8091',warmup:10,weight:50,status:true,timestamp:1637826588267,\"gray\":false}]";
+
+        List<URIRegisterDTO> list = new ArrayList<>();
+        list.add(URIRegisterDTO.builder().appName("test1")
+                .rpcType(RpcTypeEnum.DUBBO.getName())
+                
.host(LOCALHOST).port(8090).eventType(EventType.DELETED).build());
+        SelectorDO selectorDO = mock(SelectorDO.class);
+        when(selectorDO.getHandle()).thenReturn(returnStr);
+        doReturn(false).when(shenyuClientRegisterDubboService).doSubmit(any(), 
any());
+        String actual = shenyuClientRegisterDubboService.buildHandle(list, 
selectorDO);
+
+        List<DubboUpstream> resultList = 
GsonUtils.getInstance().fromCurrentList(actual, DubboUpstream.class);
+        assertEquals(2, resultList.size());
+        assertFalse(resultList.get(0).isStatus());
+        assertTrue(resultList.get(1).isStatus());
+    }
+
+    /**
+     * An offline upstream coming back online should be written back into the 
selector handle as well,
+     * and only the re-registered one should be touched.
+     * see <a href="https://github.com/apache/shenyu/issues/6522";>issue 
#6522</a>
+     */
+    @Test
+    public void testBuildHandleWithStatusRecovered() {
+        shenyuClientRegisterDubboService = 
spy(shenyuClientRegisterDubboService);
+
+        final String returnStr = 
"[{protocol:'dubbo://',upstreamHost:'localhost',upstreamUrl:'localhost:8090',warmup:10,weight:50,status:false,timestamp:1637826588267,\"gray\":false},"
+                + 
"{protocol:'dubbo://',upstreamHost:'localhost',upstreamUrl:'localhost:8091',warmup:10,weight:50,status:false,timestamp:1637826588267,\"gray\":false}]";
+
+        List<URIRegisterDTO> list = new ArrayList<>();
+        list.add(URIRegisterDTO.builder().appName("test1")
+                .rpcType(RpcTypeEnum.DUBBO.getName())
+                .host(LOCALHOST).port(8090).build());
+        SelectorDO selectorDO = mock(SelectorDO.class);
+        when(selectorDO.getHandle()).thenReturn(returnStr);
+        doReturn(false).when(shenyuClientRegisterDubboService).doSubmit(any(), 
any());
+        String actual = shenyuClientRegisterDubboService.buildHandle(list, 
selectorDO);
+
+        List<DubboUpstream> resultList = 
GsonUtils.getInstance().fromCurrentList(actual, DubboUpstream.class);
+        assertEquals(2, resultList.size());
+        assertTrue(resultList.get(0).isStatus());
+        assertFalse(resultList.get(1).isStatus());
+    }
+
     @Test
     public void testBuildDivideUpstreamList() {
         List<URIRegisterDTO> list = new ArrayList<>();
diff --git 
a/shenyu-admin/src/test/java/org/apache/shenyu/admin/service/register/ShenyuClientRegisterGrpcServiceImplTest.java
 
b/shenyu-admin/src/test/java/org/apache/shenyu/admin/service/register/ShenyuClientRegisterGrpcServiceImplTest.java
index 7e731fadb8..bc53b41d8d 100644
--- 
a/shenyu-admin/src/test/java/org/apache/shenyu/admin/service/register/ShenyuClientRegisterGrpcServiceImplTest.java
+++ 
b/shenyu-admin/src/test/java/org/apache/shenyu/admin/service/register/ShenyuClientRegisterGrpcServiceImplTest.java
@@ -21,12 +21,14 @@ import org.apache.commons.lang3.StringUtils;
 import org.apache.shenyu.admin.model.entity.MetaDataDO;
 import org.apache.shenyu.admin.model.entity.SelectorDO;
 import org.apache.shenyu.admin.service.impl.MetaDataServiceImpl;
+import org.apache.shenyu.common.dto.convert.selector.GrpcUpstream;
 import org.apache.shenyu.common.dto.convert.selector.TarsUpstream;
 import org.apache.shenyu.common.enums.RpcTypeEnum;
 import org.apache.shenyu.common.exception.ShenyuException;
 import org.apache.shenyu.common.utils.GsonUtils;
 import org.apache.shenyu.register.common.dto.MetaDataRegisterDTO;
 import org.apache.shenyu.register.common.dto.URIRegisterDTO;
+import org.apache.shenyu.register.common.enums.EventType;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.ExtendWith;
 import org.mockito.InjectMocks;
@@ -41,6 +43,8 @@ import java.util.List;
 
 import static 
org.apache.shenyu.common.constant.Constants.SYS_DEFAULT_NAMESPACE_ID;
 import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
@@ -130,6 +134,57 @@ public final class ShenyuClientRegisterGrpcServiceImplTest 
{
         assertEquals(resultList.size(), 1);
     }
     
+    /**
+     * The status change of an existing upstream should be written back into 
the selector handle.
+     * see <a href="https://github.com/apache/shenyu/issues/6522";>issue 
#6522</a>
+     */
+    @Test
+    public void testBuildHandleWithStatusChanged() {
+        shenyuClientRegisterGrpcService = spy(shenyuClientRegisterGrpcService);
+
+        final String returnStr = 
"[{upstreamUrl='localhost:8090',weight=1,status=true,timestamp=1637826588267,\"gray\":false},"
+                + 
"{upstreamUrl='localhost:8091',weight=2,status=true,timestamp=1637826588267,\"gray\":false}]";
+
+        List<URIRegisterDTO> list = new ArrayList<>();
+        
list.add(URIRegisterDTO.builder().appName("test1").rpcType(RpcTypeEnum.GRPC.getName())
+                
.host("localhost").port(8090).eventType(EventType.DELETED).build());
+        SelectorDO selectorDO = mock(SelectorDO.class);
+        when(selectorDO.getHandle()).thenReturn(returnStr);
+        doReturn(false).when(shenyuClientRegisterGrpcService).doSubmit(any(), 
any());
+        String actual = shenyuClientRegisterGrpcService.buildHandle(list, 
selectorDO);
+
+        List<GrpcUpstream> resultList = 
GsonUtils.getInstance().fromCurrentList(actual, GrpcUpstream.class);
+        assertEquals(2, resultList.size());
+        assertFalse(resultList.get(0).isStatus());
+        assertTrue(resultList.get(1).isStatus());
+    }
+
+    /**
+     * An offline upstream coming back online should be written back into the 
selector handle as well,
+     * and only the re-registered one should be touched.
+     * see <a href="https://github.com/apache/shenyu/issues/6522";>issue 
#6522</a>
+     */
+    @Test
+    public void testBuildHandleWithStatusRecovered() {
+        shenyuClientRegisterGrpcService = spy(shenyuClientRegisterGrpcService);
+
+        final String returnStr = 
"[{upstreamUrl='localhost:8090',weight=1,status=false,timestamp=1637826588267,\"gray\":false},"
+                + 
"{upstreamUrl='localhost:8091',weight=2,status=false,timestamp=1637826588267,\"gray\":false}]";
+
+        List<URIRegisterDTO> list = new ArrayList<>();
+        
list.add(URIRegisterDTO.builder().appName("test1").rpcType(RpcTypeEnum.GRPC.getName())
+                .host("localhost").port(8090).build());
+        SelectorDO selectorDO = mock(SelectorDO.class);
+        when(selectorDO.getHandle()).thenReturn(returnStr);
+        doReturn(false).when(shenyuClientRegisterGrpcService).doSubmit(any(), 
any());
+        String actual = shenyuClientRegisterGrpcService.buildHandle(list, 
selectorDO);
+
+        List<GrpcUpstream> resultList = 
GsonUtils.getInstance().fromCurrentList(actual, GrpcUpstream.class);
+        assertEquals(2, resultList.size());
+        assertTrue(resultList.get(0).isStatus());
+        assertFalse(resultList.get(1).isStatus());
+    }
+
     @Test
     public void testBuildGrpcUpstreamList() {
         List<URIRegisterDTO> list = new ArrayList<>();
diff --git 
a/shenyu-admin/src/test/java/org/apache/shenyu/admin/service/register/ShenyuClientRegisterTarsServiceImplTest.java
 
b/shenyu-admin/src/test/java/org/apache/shenyu/admin/service/register/ShenyuClientRegisterTarsServiceImplTest.java
index 8b3c264dbe..eea20536a9 100644
--- 
a/shenyu-admin/src/test/java/org/apache/shenyu/admin/service/register/ShenyuClientRegisterTarsServiceImplTest.java
+++ 
b/shenyu-admin/src/test/java/org/apache/shenyu/admin/service/register/ShenyuClientRegisterTarsServiceImplTest.java
@@ -27,6 +27,7 @@ import org.apache.shenyu.common.exception.ShenyuException;
 import org.apache.shenyu.common.utils.GsonUtils;
 import org.apache.shenyu.register.common.dto.MetaDataRegisterDTO;
 import org.apache.shenyu.register.common.dto.URIRegisterDTO;
+import org.apache.shenyu.register.common.enums.EventType;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.ExtendWith;
 import org.mockito.InjectMocks;
@@ -41,6 +42,8 @@ import java.util.List;
 
 import static 
org.apache.shenyu.common.constant.Constants.SYS_DEFAULT_NAMESPACE_ID;
 import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
@@ -132,6 +135,57 @@ public final class ShenyuClientRegisterTarsServiceImplTest 
{
         assertEquals(resultList.size(), 1);
     }
     
+    /**
+     * The status change of an existing upstream should be written back into 
the selector handle.
+     * see <a href="https://github.com/apache/shenyu/issues/6522";>issue 
#6522</a>
+     */
+    @Test
+    public void testBuildHandleWithStatusChanged() {
+        shenyuClientRegisterTarsService = spy(shenyuClientRegisterTarsService);
+
+        final String returnStr = 
"[{upstreamUrl:'localhost:8090',weight:1,warmup:10,status:true,timestamp:1637826588267,\"gray\":false},"
+                + 
"{upstreamUrl:'localhost:8091',weight:2,warmup:10,status:true,timestamp:1637826588267,\"gray\":false}]";
+
+        List<URIRegisterDTO> list = new ArrayList<>();
+        
list.add(URIRegisterDTO.builder().appName("test1").rpcType(RpcTypeEnum.TARS.getName())
+                
.host("localhost").port(8090).eventType(EventType.DELETED).build());
+        SelectorDO selectorDO = mock(SelectorDO.class);
+        when(selectorDO.getHandle()).thenReturn(returnStr);
+        doReturn(false).when(shenyuClientRegisterTarsService).doSubmit(any(), 
any());
+        String actual = shenyuClientRegisterTarsService.buildHandle(list, 
selectorDO);
+
+        List<TarsUpstream> resultList = 
GsonUtils.getInstance().fromCurrentList(actual, TarsUpstream.class);
+        assertEquals(2, resultList.size());
+        assertFalse(resultList.get(0).isStatus());
+        assertTrue(resultList.get(1).isStatus());
+    }
+
+    /**
+     * An offline upstream coming back online should be written back into the 
selector handle as well,
+     * and only the re-registered one should be touched.
+     * see <a href="https://github.com/apache/shenyu/issues/6522";>issue 
#6522</a>
+     */
+    @Test
+    public void testBuildHandleWithStatusRecovered() {
+        shenyuClientRegisterTarsService = spy(shenyuClientRegisterTarsService);
+
+        final String returnStr = 
"[{upstreamUrl:'localhost:8090',weight:1,warmup:10,status:false,timestamp:1637826588267,\"gray\":false},"
+                + 
"{upstreamUrl:'localhost:8091',weight:2,warmup:10,status:false,timestamp:1637826588267,\"gray\":false}]";
+
+        List<URIRegisterDTO> list = new ArrayList<>();
+        
list.add(URIRegisterDTO.builder().appName("test1").rpcType(RpcTypeEnum.TARS.getName())
+                .host("localhost").port(8090).build());
+        SelectorDO selectorDO = mock(SelectorDO.class);
+        when(selectorDO.getHandle()).thenReturn(returnStr);
+        doReturn(false).when(shenyuClientRegisterTarsService).doSubmit(any(), 
any());
+        String actual = shenyuClientRegisterTarsService.buildHandle(list, 
selectorDO);
+
+        List<TarsUpstream> resultList = 
GsonUtils.getInstance().fromCurrentList(actual, TarsUpstream.class);
+        assertEquals(2, resultList.size());
+        assertTrue(resultList.get(0).isStatus());
+        assertFalse(resultList.get(1).isStatus());
+    }
+
     @Test
     public void testBuildTarsUpstreamList() {
         List<URIRegisterDTO> list = new ArrayList<>();
diff --git 
a/shenyu-admin/src/test/java/org/apache/shenyu/admin/service/register/ShenyuClientRegisterWebSocketServiceImplTest.java
 
b/shenyu-admin/src/test/java/org/apache/shenyu/admin/service/register/ShenyuClientRegisterWebSocketServiceImplTest.java
index 3da5ab7cc8..2296d22e71 100644
--- 
a/shenyu-admin/src/test/java/org/apache/shenyu/admin/service/register/ShenyuClientRegisterWebSocketServiceImplTest.java
+++ 
b/shenyu-admin/src/test/java/org/apache/shenyu/admin/service/register/ShenyuClientRegisterWebSocketServiceImplTest.java
@@ -37,10 +37,14 @@ import org.mockito.junit.jupiter.MockitoExtension;
 import org.mockito.junit.jupiter.MockitoSettings;
 import org.mockito.quality.Strictness;
 
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
 
+import static 
org.apache.shenyu.common.constant.Constants.SYS_DEFAULT_NAMESPACE_ID;
 import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
@@ -127,4 +131,31 @@ class ShenyuClientRegisterWebSocketServiceImplTest {
         assertEquals(webSocketUpstreamResult.get("upstreamHost"), "localhost");
         assertEquals(webSocketUpstreamResult.get("status"), true);
     }
+
+    /**
+     * An offline upstream coming back online should be written back into the 
selector handle as well,
+     * and only the re-registered one should be touched.
+     * see <a href="https://github.com/apache/shenyu/issues/6522";>issue 
#6522</a>
+     */
+    @Test
+    public void testBuildHandleWithStatusRecovered() {
+        when(upstreamCheckService.checkAndSubmit(any(), 
any())).thenReturn(false);
+        SelectorDO selectorDO = new SelectorDO();
+        selectorDO.setHandle(GsonUtils.getInstance().toJson(Arrays.asList(
+                offlineUpstream("host:8080"), offlineUpstream("host:8081"))));
+
+        List<URIRegisterDTO> uriList = 
Collections.singletonList(URIRegisterDTO.builder()
+                
.protocol("ws://").host("host").port(8080).namespaceId(SYS_DEFAULT_NAMESPACE_ID).build());
+        String actual = 
shenyuClientRegisterWebSocketService.buildHandle(uriList, selectorDO);
+
+        List<WebSocketUpstream> resultList = 
GsonUtils.getInstance().fromCurrentList(actual, WebSocketUpstream.class);
+        assertEquals(2, resultList.size());
+        assertTrue(resultList.get(0).isStatus());
+        assertFalse(resultList.get(1).isStatus());
+    }
+
+    private static WebSocketUpstream offlineUpstream(final String url) {
+        return 
WebSocketUpstream.builder().host("localhost").protocol("ws://").upstreamUrl(url)
+                
.weight(50).warmup(10).namespaceId(SYS_DEFAULT_NAMESPACE_ID).status(false).build();
+    }
 }

Reply via email to