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

albumenj pushed a commit to branch 3.2
in repository https://gitbox.apache.org/repos/asf/dubbo.git


The following commit(s) were added to refs/heads/3.2 by this push:
     new 5ef3b4fb2e Add dual chain support for state router, prevent update 
when invoke (#10691)
5ef3b4fb2e is described below

commit 5ef3b4fb2e6ac387fa3a0927eb11c5bd8cd6bea8
Author: Albumen Kevin <[email protected]>
AuthorDate: Fri Oct 14 10:32:20 2022 +0800

    Add dual chain support for state router, prevent update when invoke (#10691)
    
    * Add dual chain support for state router, prevent update when invoke
    
    * Fix uts
---
 .../org/apache/dubbo/rpc/cluster/RouterChain.java  | 334 +++++----------------
 .../{RouterChain.java => SingleRouterChain.java}   |  56 ++--
 .../cluster/router/state/AbstractStateRouter.java  |   9 +-
 .../dubbo/rpc/cluster/router/state/BitList.java    |   5 +-
 .../rpc/cluster/directory/StaticDirectoryTest.java |   2 +-
 .../router/condition/ConditionStateRouterTest.java |  44 +--
 .../cluster/router/file/FileRouterEngineTest.java  |   2 +-
 .../router/script/ScriptStateRouterTest.java       |   8 +-
 .../rpc/cluster/router/tag/TagStateRouterTest.java |   3 +-
 .../common/constants/LoggerCodeConstants.java      |   5 +
 10 files changed, 131 insertions(+), 337 deletions(-)

diff --git 
a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/RouterChain.java 
b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/RouterChain.java
index 64246b9ea5..877f16032e 100644
--- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/RouterChain.java
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/RouterChain.java
@@ -17,34 +17,23 @@
 package org.apache.dubbo.rpc.cluster;
 
 import org.apache.dubbo.common.URL;
-import org.apache.dubbo.common.Version;
 import org.apache.dubbo.common.config.ConfigurationUtils;
 import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
 import org.apache.dubbo.common.logger.LoggerFactory;
-import org.apache.dubbo.common.utils.CollectionUtils;
-import org.apache.dubbo.common.utils.Holder;
-import org.apache.dubbo.common.utils.NetUtils;
 import org.apache.dubbo.rpc.Invocation;
 import org.apache.dubbo.rpc.Invoker;
-import org.apache.dubbo.rpc.RpcContext;
-import org.apache.dubbo.rpc.cluster.router.RouterResult;
-import org.apache.dubbo.rpc.cluster.router.RouterSnapshotNode;
 import org.apache.dubbo.rpc.cluster.router.RouterSnapshotSwitcher;
 import org.apache.dubbo.rpc.cluster.router.state.BitList;
 import org.apache.dubbo.rpc.cluster.router.state.StateRouter;
 import org.apache.dubbo.rpc.cluster.router.state.StateRouterFactory;
-import org.apache.dubbo.rpc.cluster.router.state.TailStateRouter;
 import org.apache.dubbo.rpc.model.ModuleModel;
 import org.apache.dubbo.rpc.model.ScopeModelUtil;
 
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.LinkedList;
 import java.util.List;
 import java.util.stream.Collectors;
 
-import static 
org.apache.dubbo.common.constants.LoggerCodeConstants.CLUSTER_FAILED_STOP;
-import static 
org.apache.dubbo.common.constants.LoggerCodeConstants.CLUSTER_NO_VALID_PROVIDER;
+import static 
org.apache.dubbo.common.constants.LoggerCodeConstants.INTERNAL_INTERRUPTED;
+import static 
org.apache.dubbo.common.constants.LoggerCodeConstants.REGISTRY_ROUTER_WAIT_LONG;
 import static org.apache.dubbo.rpc.cluster.Constants.ROUTER_KEY;
 
 /**
@@ -53,34 +42,18 @@ import static 
org.apache.dubbo.rpc.cluster.Constants.ROUTER_KEY;
 public class RouterChain<T> {
     private static final ErrorTypeAwareLogger logger = 
LoggerFactory.getErrorTypeAwareLogger(RouterChain.class);
 
-    /**
-     * full list of addresses from registry, classified by method name.
-     */
-    private volatile BitList<Invoker<T>> invokers = BitList.emptyList();
-
-    /**
-     * containing all routers, reconstruct every time 'route://' urls change.
-     */
-    private volatile List<Router> routers = Collections.emptyList();
-
-    /**
-     * Fixed router instances: ConfigConditionRouter, TagRouter, e.g.,
-     * the rule for each instance may change but the instance will never 
delete or recreate.
-     */
-    private volatile List<Router> builtinRouters = Collections.emptyList();
-
-    private volatile StateRouter<T> headStateRouter;
-
-    private volatile List<StateRouter<T>> stateRouters;
-
-    /**
-     * Should continue route if current router's result is empty
-     */
-    private final boolean shouldFailFast;
-
-    private final RouterSnapshotSwitcher routerSnapshotSwitcher;
+    private final SingleRouterChain<T> mainChain;
+    private final SingleRouterChain<T> backupChain;
+    private volatile SingleRouterChain<T> currentChain;
 
+    @SuppressWarnings({"rawtypes", "unchecked"})
     public static <T> RouterChain<T> buildChain(Class<T> interfaceClass, URL 
url) {
+        SingleRouterChain<T> chain1 = buildSingleChain(interfaceClass, url);
+        SingleRouterChain<T> chain2 = buildSingleChain(interfaceClass, url);
+        return new RouterChain<>(new SingleRouterChain[]{chain1, chain2});
+    }
+
+    public static <T> SingleRouterChain<T> buildSingleChain(Class<T> 
interfaceClass, URL url) {
         ModuleModel moduleModel = url.getOrDefaultModuleModel();
 
         List<RouterFactory> extensionFactories = 
moduleModel.getExtensionLoader(RouterFactory.class)
@@ -103,264 +76,95 @@ public class RouterChain<T> {
 
         RouterSnapshotSwitcher routerSnapshotSwitcher = 
ScopeModelUtil.getFrameworkModel(moduleModel).getBeanFactory().getBean(RouterSnapshotSwitcher.class);
 
-        return new RouterChain<>(routers, stateRouters, shouldFailFast, 
routerSnapshotSwitcher);
+        return new SingleRouterChain<>(routers, stateRouters, shouldFailFast, 
routerSnapshotSwitcher);
     }
 
-    public RouterChain(List<Router> routers, List<StateRouter<T>> 
stateRouters, boolean shouldFailFast, RouterSnapshotSwitcher 
routerSnapshotSwitcher) {
-        initWithRouters(routers);
-
-        initWithStateRouters(stateRouters);
-
-        this.shouldFailFast = shouldFailFast;
-        this.routerSnapshotSwitcher = routerSnapshotSwitcher;
-    }
-
-    private void initWithStateRouters(List<StateRouter<T>> stateRouters) {
-        StateRouter<T> stateRouter = TailStateRouter.getInstance();
-        for (int i = stateRouters.size() - 1; i >= 0; i--) {
-            StateRouter<T> nextStateRouter = stateRouters.get(i);
-            nextStateRouter.setNextRouter(stateRouter);
-            stateRouter = nextStateRouter;
+    public RouterChain(SingleRouterChain<T>[] chains) {
+        if (chains.length != 2) {
+            throw new IllegalArgumentException("chains' size should be 2.");
         }
-        this.headStateRouter = stateRouter;
-        this.stateRouters = Collections.unmodifiableList(stateRouters);
+        this.mainChain = chains[0];
+        this.backupChain = chains[1];
+        this.currentChain = this.mainChain;
     }
 
-    /**
-     * the resident routers must being initialized before address notification.
-     * only for ut
-     */
-    public void initWithRouters(List<Router> builtinRouters) {
-        this.builtinRouters = builtinRouters;
-        this.routers = new LinkedList<>(builtinRouters);
+    public List<Invoker<T>> route(URL url, BitList<Invoker<T>> 
availableInvokers, Invocation invocation) {
+        return currentChain.route(url, availableInvokers, invocation);
     }
 
     /**
-     * If we use route:// protocol in version before 2.7.0, each URL will 
generate a Router instance, so we should
-     * keep the routers up to date, that is, each time router URLs changes, we 
should update the routers list, only
-     * keep the builtinRouters which are available all the time and the latest 
notified routers which are generated
-     * from URLs.
-     *
-     * @param routers routers from 'router://' rules in 2.6.x or before.
+     * Notify router chain of the initial addresses from registry at the first 
time.
+     * Notify whenever addresses in registry change.
      */
-    public void addRouters(List<Router> routers) {
-        List<Router> newRouters = new LinkedList<>();
-        newRouters.addAll(builtinRouters);
-        newRouters.addAll(routers);
-        CollectionUtils.sort(newRouters);
-        this.routers = newRouters;
-    }
-
-    public List<Router> getRouters() {
-        return routers;
-    }
-
-    public StateRouter<T> getHeadStateRouter() {
-        return headStateRouter;
-    }
-
-    public List<Invoker<T>> route(URL url, BitList<Invoker<T>> 
availableInvokers, Invocation invocation) {
-        if (RpcContext.getServiceContext().isNeedPrintRouterSnapshot()) {
-            return routeAndPrint(url, availableInvokers, invocation);
-        } else {
-            return simpleRoute(url, availableInvokers, invocation);
-        }
-    }
+    public synchronized void setInvokers(BitList<Invoker<T>> invokers) {
+        // 1. switch
+        currentChain = backupChain;
 
-    public List<Invoker<T>> routeAndPrint(URL url, BitList<Invoker<T>> 
availableInvokers, Invocation invocation) {
-        RouterSnapshotNode<T> snapshot = buildRouterSnapshot(url, 
availableInvokers, invocation);
-        logRouterSnapshot(url, invocation, snapshot);
-        return snapshot.getChainOutputInvokers();
-    }
-
-    public List<Invoker<T>> simpleRoute(URL url, BitList<Invoker<T>> 
availableInvokers, Invocation invocation) {
-        BitList<Invoker<T>> resultInvokers = availableInvokers.clone();
+        // 2. wait
+        waitChain(mainChain);
 
-        // 1. route state router
-        resultInvokers = headStateRouter.route(resultInvokers, url, 
invocation, false, null);
-        if (resultInvokers.isEmpty() && (shouldFailFast || routers.isEmpty())) 
{
-            printRouterSnapshot(url, availableInvokers, invocation);
-            return BitList.emptyList();
-        }
+        // 3. notify
+        mainChain.setInvokers(invokers);
 
-        if (routers.isEmpty()) {
-            return resultInvokers;
-        }
-        List<Invoker<T>> commonRouterResult = 
resultInvokers.cloneToArrayList();
-        // 2. route common router
-        for (Router router : routers) {
-            // Copy resultInvokers to a arrayList. BitList not support
-            RouterResult<Invoker<T>> routeResult = 
router.route(commonRouterResult, url, invocation, false);
-            commonRouterResult = routeResult.getResult();
-            if (CollectionUtils.isEmpty(commonRouterResult) && shouldFailFast) 
{
-                printRouterSnapshot(url, availableInvokers, invocation);
-                return BitList.emptyList();
-            }
-
-            // stop continue routing
-            if (!routeResult.isNeedContinueRoute()) {
-                return commonRouterResult;
-            }
-        }
+        // 4. switch back
+        currentChain = mainChain;
 
-        if (commonRouterResult.isEmpty()) {
-            printRouterSnapshot(url, availableInvokers, invocation);
-            return BitList.emptyList();
-        }
+        // 5. wait
+        waitChain(backupChain);
 
-        return commonRouterResult;
+        // 6. notify
+        backupChain.setInvokers(invokers);
     }
 
-    /**
-     * store each router's input and output, log out if empty
-     */
-    private void printRouterSnapshot(URL url, BitList<Invoker<T>> 
availableInvokers, Invocation invocation) {
-        if (logger.isWarnEnabled()) {
-            logRouterSnapshot(url, invocation, buildRouterSnapshot(url, 
availableInvokers, invocation));
+    private void waitChain(SingleRouterChain<T> oldChain) {
+        try {
+            Thread.sleep(1);
+            int waitTime = 0;
+            while (oldChain.getCurrentConcurrency() != 0) {
+                if (waitTime++ == 1000) {
+                    logger.warn(REGISTRY_ROUTER_WAIT_LONG, "Wait router to 
long", "", "Wait router invoke end exceed 1000ms, router may stuck in.");
+                }
+                // long time wait
+                Thread.sleep(1);
+            }
+        } catch (Throwable t) {
+            logger.error(INTERNAL_INTERRUPTED, "Wait router to interrupted", 
"", "Wait router to interrupted.");
         }
     }
 
-    /**
-     * Build each router's result
-     */
-    public RouterSnapshotNode<T> buildRouterSnapshot(URL url, 
BitList<Invoker<T>> availableInvokers, Invocation invocation) {
-        BitList<Invoker<T>> resultInvokers = availableInvokers.clone();
-        RouterSnapshotNode<T> parentNode = new RouterSnapshotNode<T>("Parent", 
resultInvokers.clone());
-        parentNode.setNodeOutputInvokers(resultInvokers.clone());
-
-        // 1. route state router
-        Holder<RouterSnapshotNode<T>> nodeHolder = new Holder<>();
-        nodeHolder.set(parentNode);
-
-        resultInvokers = headStateRouter.route(resultInvokers, url, 
invocation, true, nodeHolder);
-
-        // result is empty, log out
-        if (routers.isEmpty() || (resultInvokers.isEmpty() && shouldFailFast)) 
{
-            parentNode.setChainOutputInvokers(resultInvokers.clone());
-            return parentNode;
-        }
-
-        RouterSnapshotNode<T> commonRouterNode = new 
RouterSnapshotNode<T>("CommonRouter", resultInvokers.clone());
-        parentNode.appendNode(commonRouterNode);
-        List<Invoker<T>> commonRouterResult = resultInvokers;
+    public synchronized void destroy() {
+        // 1. destroy another
+        backupChain.destroy();
 
-        // 2. route common router
-        for (Router router : routers) {
-            // Copy resultInvokers to a arrayList. BitList not support
-            List<Invoker<T>> inputInvokers = new 
ArrayList<>(commonRouterResult);
+        // 2. switch
+        currentChain = backupChain;
 
-            RouterSnapshotNode<T> currentNode = new 
RouterSnapshotNode<T>(router.getClass().getSimpleName(), inputInvokers);
+        // 3. wait
+        waitChain(mainChain);
 
-            // append to router node chain
-            commonRouterNode.appendNode(currentNode);
-            commonRouterNode = currentNode;
-
-            RouterResult<Invoker<T>> routeStateResult = 
router.route(inputInvokers, url, invocation, true);
-            List<Invoker<T>> routeResult = routeStateResult.getResult();
-            String routerMessage = routeStateResult.getMessage();
-
-            currentNode.setNodeOutputInvokers(routeResult);
-            currentNode.setRouterMessage(routerMessage);
-
-            commonRouterResult = routeResult;
-
-            // result is empty, log out
-            if (CollectionUtils.isEmpty(routeResult) && shouldFailFast) {
-                break;
-            }
+        // 4. destroy
+        mainChain.destroy();
+    }
 
-            if (!routeStateResult.isNeedContinueRoute()) {
-                break;
-            }
-        }
-        
commonRouterNode.setChainOutputInvokers(commonRouterNode.getNodeOutputInvokers());
-
-        // 3. set router chain output reverse
-        RouterSnapshotNode<T> currentNode = commonRouterNode;
-        while (currentNode != null){
-            RouterSnapshotNode<T> parent = currentNode.getParentNode();
-            if (parent != null) {
-                // common router only has one child invoke
-                
parent.setChainOutputInvokers(currentNode.getChainOutputInvokers());
-            }
-            currentNode = parent;
-        }
-        return parentNode;
+    public void addRouters(List<Router> routers) {
+        mainChain.addRouters(routers);
+        backupChain.addRouters(routers);
     }
 
-    private void logRouterSnapshot(URL url, Invocation invocation, 
RouterSnapshotNode<T> snapshotNode) {
-        if (snapshotNode.getChainOutputInvokers() == null ||
-            snapshotNode.getChainOutputInvokers().isEmpty()) {
-            if (logger.isWarnEnabled()) {
-                String message = "No provider available after route for the 
service " + url.getServiceKey()
-                    + " from registry " + url.getAddress()
-                    + " on the consumer " + NetUtils.getLocalHost()
-                    + " using the dubbo version " + Version.getVersion() + ". 
Router snapshot is below: \n" + snapshotNode.toString();
-                if (routerSnapshotSwitcher.isEnable()) {
-                    routerSnapshotSwitcher.setSnapshot(message);
-                }
-                logger.warn(CLUSTER_NO_VALID_PROVIDER,"No provider available 
after route for the service","",message);
-            }
-        } else {
-            if (logger.isInfoEnabled()) {
-                String message = "Router snapshot service " + 
url.getServiceKey()
-                    + " from registry " + url.getAddress()
-                    + " on the consumer " + NetUtils.getLocalHost()
-                    + " using the dubbo version " + Version.getVersion() + " 
is below: \n" + snapshotNode.toString();
-                if (routerSnapshotSwitcher.isEnable()) {
-                    routerSnapshotSwitcher.setSnapshot(message);
-                }
-                logger.info(message);
-            }
-        }
+    public SingleRouterChain<T> getCurrentChain() {
+        return currentChain;
     }
 
-    /**
-     * Notify router chain of the initial addresses from registry at the first 
time.
-     * Notify whenever addresses in registry change.
-     */
-    public void setInvokers(BitList<Invoker<T>> invokers) {
-        this.invokers = (invokers == null ? BitList.emptyList() : invokers);
-        routers.forEach(router -> router.notify(this.invokers));
-        stateRouters.forEach(router -> router.notify(this.invokers));
+    public List<Router> getRouters() {
+        return currentChain.getRouters();
     }
 
-    /**
-     * for uts only
-     */
-    @Deprecated
-    public void setHeadStateRouter(StateRouter<T> headStateRouter) {
-        this.headStateRouter = headStateRouter;
+    public StateRouter<T> getHeadStateRouter() {
+        return currentChain.getHeadStateRouter();
     }
 
-    /**
-     * for uts only
-     */
     @Deprecated
     public List<StateRouter<T>> getStateRouters() {
-        return stateRouters;
-    }
-
-    public void destroy() {
-        invokers = BitList.emptyList();
-        for (Router router : routers) {
-            try {
-                router.stop();
-            } catch (Exception e) {
-                logger.error(CLUSTER_FAILED_STOP,"route stop failed","","Error 
trying to stop router " + router.getClass(),e);
-            }
-        }
-        routers = Collections.emptyList();
-        builtinRouters = Collections.emptyList();
-
-        for (StateRouter<T> router : stateRouters) {
-            try {
-                router.stop();
-            } catch (Exception e) {
-                logger.error(CLUSTER_FAILED_STOP,"StateRouter stop 
failed","","Error trying to stop StateRouter " + router.getClass(),e);
-            }
-        }
-        stateRouters = Collections.emptyList();
-        headStateRouter = TailStateRouter.getInstance();
+        return currentChain.getStateRouters();
     }
 }
diff --git 
a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/RouterChain.java 
b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/SingleRouterChain.java
similarity index 86%
copy from 
dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/RouterChain.java
copy to 
dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/SingleRouterChain.java
index 64246b9ea5..73e74459a5 100644
--- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/RouterChain.java
+++ 
b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/SingleRouterChain.java
@@ -18,7 +18,6 @@ package org.apache.dubbo.rpc.cluster;
 
 import org.apache.dubbo.common.URL;
 import org.apache.dubbo.common.Version;
-import org.apache.dubbo.common.config.ConfigurationUtils;
 import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
 import org.apache.dubbo.common.logger.LoggerFactory;
 import org.apache.dubbo.common.utils.CollectionUtils;
@@ -32,26 +31,22 @@ import 
org.apache.dubbo.rpc.cluster.router.RouterSnapshotNode;
 import org.apache.dubbo.rpc.cluster.router.RouterSnapshotSwitcher;
 import org.apache.dubbo.rpc.cluster.router.state.BitList;
 import org.apache.dubbo.rpc.cluster.router.state.StateRouter;
-import org.apache.dubbo.rpc.cluster.router.state.StateRouterFactory;
 import org.apache.dubbo.rpc.cluster.router.state.TailStateRouter;
-import org.apache.dubbo.rpc.model.ModuleModel;
-import org.apache.dubbo.rpc.model.ScopeModelUtil;
 
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.LinkedList;
 import java.util.List;
-import java.util.stream.Collectors;
+import java.util.concurrent.atomic.AtomicInteger;
 
 import static 
org.apache.dubbo.common.constants.LoggerCodeConstants.CLUSTER_FAILED_STOP;
 import static 
org.apache.dubbo.common.constants.LoggerCodeConstants.CLUSTER_NO_VALID_PROVIDER;
-import static org.apache.dubbo.rpc.cluster.Constants.ROUTER_KEY;
 
 /**
  * Router chain
  */
-public class RouterChain<T> {
-    private static final ErrorTypeAwareLogger logger = 
LoggerFactory.getErrorTypeAwareLogger(RouterChain.class);
+public class SingleRouterChain<T> {
+    private static final ErrorTypeAwareLogger logger = 
LoggerFactory.getErrorTypeAwareLogger(SingleRouterChain.class);
 
     /**
      * full list of addresses from registry, classified by method name.
@@ -80,33 +75,9 @@ public class RouterChain<T> {
 
     private final RouterSnapshotSwitcher routerSnapshotSwitcher;
 
-    public static <T> RouterChain<T> buildChain(Class<T> interfaceClass, URL 
url) {
-        ModuleModel moduleModel = url.getOrDefaultModuleModel();
+    private final AtomicInteger currentConcurrency = new AtomicInteger(0);
 
-        List<RouterFactory> extensionFactories = 
moduleModel.getExtensionLoader(RouterFactory.class)
-            .getActivateExtension(url, ROUTER_KEY);
-
-        List<Router> routers = extensionFactories.stream()
-            .map(factory -> factory.getRouter(url))
-            .sorted(Router::compareTo)
-            .collect(Collectors.toList());
-
-        List<StateRouter<T>> stateRouters = moduleModel
-            .getExtensionLoader(StateRouterFactory.class)
-            .getActivateExtension(url, ROUTER_KEY)
-            .stream()
-            .map(factory -> factory.getRouter(interfaceClass, url))
-            .collect(Collectors.toList());
-
-
-        boolean shouldFailFast = 
Boolean.parseBoolean(ConfigurationUtils.getProperty(moduleModel, 
Constants.SHOULD_FAIL_FAST_KEY, "true"));
-
-        RouterSnapshotSwitcher routerSnapshotSwitcher = 
ScopeModelUtil.getFrameworkModel(moduleModel).getBeanFactory().getBean(RouterSnapshotSwitcher.class);
-
-        return new RouterChain<>(routers, stateRouters, shouldFailFast, 
routerSnapshotSwitcher);
-    }
-
-    public RouterChain(List<Router> routers, List<StateRouter<T>> 
stateRouters, boolean shouldFailFast, RouterSnapshotSwitcher 
routerSnapshotSwitcher) {
+    public SingleRouterChain(List<Router> routers, List<StateRouter<T>> 
stateRouters, boolean shouldFailFast, RouterSnapshotSwitcher 
routerSnapshotSwitcher) {
         initWithRouters(routers);
 
         initWithStateRouters(stateRouters);
@@ -160,10 +131,15 @@ public class RouterChain<T> {
     }
 
     public List<Invoker<T>> route(URL url, BitList<Invoker<T>> 
availableInvokers, Invocation invocation) {
-        if (RpcContext.getServiceContext().isNeedPrintRouterSnapshot()) {
-            return routeAndPrint(url, availableInvokers, invocation);
-        } else {
-            return simpleRoute(url, availableInvokers, invocation);
+        currentConcurrency.incrementAndGet();
+        try {
+            if (RpcContext.getServiceContext().isNeedPrintRouterSnapshot()) {
+                return routeAndPrint(url, availableInvokers, invocation);
+            } else {
+                return simpleRoute(url, availableInvokers, invocation);
+            }
+        } finally {
+            currentConcurrency.decrementAndGet();
         }
     }
 
@@ -341,6 +317,10 @@ public class RouterChain<T> {
         return stateRouters;
     }
 
+    public int getCurrentConcurrency() {
+        return currentConcurrency.get();
+    }
+
     public void destroy() {
         invokers = BitList.emptyList();
         for (Router router : routers) {
diff --git 
a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/state/AbstractStateRouter.java
 
b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/state/AbstractStateRouter.java
index a81a1eaacb..2da97cb2e1 100644
--- 
a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/state/AbstractStateRouter.java
+++ 
b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/state/AbstractStateRouter.java
@@ -113,15 +113,16 @@ public abstract class AbstractStateRouter<T> implements 
StateRouter<T> {
         }
         BitList<Invoker<T>> routeResult;
 
+        routeResult = doRoute(invokers, url, invocation, needToPrintMessage, 
nodeHolder, messageHolder);
+        if (routeResult != invokers) {
+            routeResult = invokers.and(routeResult);
+        }
         // check if router support call continue route by itself
         if (!supportContinueRoute()) {
-            routeResult = doRoute(invokers, url, invocation, 
needToPrintMessage, nodeHolder, messageHolder);
             // use current node's result as next node's parameter
             if (!shouldFailFast || !routeResult.isEmpty()) {
                 routeResult = continueRoute(routeResult, url, invocation, 
needToPrintMessage, nodeHolder);
             }
-        } else {
-            routeResult = doRoute(invokers, url, invocation, 
needToPrintMessage, nodeHolder, messageHolder);
         }
 
         // post-build current node
@@ -190,7 +191,7 @@ public abstract class AbstractStateRouter<T> implements 
StateRouter<T> {
     @Override
     public final String buildSnapshot() {
         return doBuildSnapshot() +
-            "            ↓ \n" +
+            "            v \n" +
             nextRouter.buildSnapshot();
     }
 
diff --git 
a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/state/BitList.java
 
b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/state/BitList.java
index ce0027e290..9694bf9f51 100644
--- 
a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/state/BitList.java
+++ 
b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/state/BitList.java
@@ -118,10 +118,13 @@ public class BitList<E> extends AbstractList<E> {
      * TailList in source bitList will be totally saved even if it is not 
appeared in the target bitList.
      *
      * @param target target bitList
-     * @return a new bitList only contains those elements contain in both two 
list and source bitList's tailList
+     * @return this bitList only contains those elements contain in both two 
list and source bitList's tailList
      */
     public BitList<E> and(BitList<E> target) {
         rootSet.and(target.rootSet);
+        if (target.getTailList() != null) {
+            target.getTailList().forEach(this::addToTailList);
+        }
         return this;
     }
 
diff --git 
a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/directory/StaticDirectoryTest.java
 
b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/directory/StaticDirectoryTest.java
index 288b7bca31..1be30ff983 100644
--- 
a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/directory/StaticDirectoryTest.java
+++ 
b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/directory/StaticDirectoryTest.java
@@ -59,7 +59,7 @@ public class StaticDirectoryTest {
         BitList<Invoker<String>> invokers = new BitList<>(originInvokers);
 
 
-        List<Invoker<String>> filteredInvokers = router.route(invokers, 
URL.valueOf("consumer://" + NetUtils.getLocalHost() + "/com.foo.BarService"), 
new RpcInvocation(), false, new Holder<>());
+        List<Invoker<String>> filteredInvokers = 
router.route(invokers.clone(), URL.valueOf("consumer://" + 
NetUtils.getLocalHost() + "/com.foo.BarService"), new RpcInvocation(), false, 
new Holder<>());
         StaticDirectory<String> staticDirectory = new 
StaticDirectory<>(filteredInvokers);
         boolean isAvailable = staticDirectory.isAvailable();
         Assertions.assertTrue(!isAvailable);
diff --git 
a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/condition/ConditionStateRouterTest.java
 
b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/condition/ConditionStateRouterTest.java
index 5698dc6785..1c6c799cad 100644
--- 
a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/condition/ConditionStateRouterTest.java
+++ 
b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/condition/ConditionStateRouterTest.java
@@ -124,12 +124,12 @@ public class ConditionStateRouterTest {
 
 
 
-        List<Invoker<String>> filteredInvokers1 = router1.route(invokers, 
URL.valueOf("consumer://" + LOCAL_HOST + "/com.foo.BarService"), new 
RpcInvocation(), false, new Holder<>());
-        List<Invoker<String>> filteredInvokers2 = router2.route(invokers, 
URL.valueOf("consumer://" + LOCAL_HOST + "/com.foo.BarService"), new 
RpcInvocation(), false, new Holder<>());
-        List<Invoker<String>> filteredInvokers3 = router3.route(invokers, 
URL.valueOf("consumer://" + LOCAL_HOST + "/com.foo.BarService"), new 
RpcInvocation(), false, new Holder<>());
-        List<Invoker<String>> filteredInvokers4 = router4.route(invokers, 
URL.valueOf("consumer://" + LOCAL_HOST + "/com.foo.BarService"), new 
RpcInvocation(), false, new Holder<>());
-        List<Invoker<String>> filteredInvokers5 = router5.route(invokers, 
URL.valueOf("consumer://" + LOCAL_HOST + "/com.foo.BarService"), new 
RpcInvocation(), false, new Holder<>());
-        List<Invoker<String>> filteredInvokers6 = router6.route(invokers, 
URL.valueOf("consumer://" + LOCAL_HOST + "/com.foo.BarService"), new 
RpcInvocation(), false, new Holder<>());
+        List<Invoker<String>> filteredInvokers1 = 
router1.route(invokers.clone(), URL.valueOf("consumer://" + LOCAL_HOST + 
"/com.foo.BarService"), new RpcInvocation(), false, new Holder<>());
+        List<Invoker<String>> filteredInvokers2 = 
router2.route(invokers.clone(), URL.valueOf("consumer://" + LOCAL_HOST + 
"/com.foo.BarService"), new RpcInvocation(), false, new Holder<>());
+        List<Invoker<String>> filteredInvokers3 = 
router3.route(invokers.clone(), URL.valueOf("consumer://" + LOCAL_HOST + 
"/com.foo.BarService"), new RpcInvocation(), false, new Holder<>());
+        List<Invoker<String>> filteredInvokers4 = 
router4.route(invokers.clone(), URL.valueOf("consumer://" + LOCAL_HOST + 
"/com.foo.BarService"), new RpcInvocation(), false, new Holder<>());
+        List<Invoker<String>> filteredInvokers5 = 
router5.route(invokers.clone(), URL.valueOf("consumer://" + LOCAL_HOST + 
"/com.foo.BarService"), new RpcInvocation(), false, new Holder<>());
+        List<Invoker<String>> filteredInvokers6 = 
router6.route(invokers.clone(), URL.valueOf("consumer://" + LOCAL_HOST + 
"/com.foo.BarService"), new RpcInvocation(), false, new Holder<>());
         Assertions.assertEquals(1, filteredInvokers1.size());
         Assertions.assertEquals(0, filteredInvokers2.size());
         Assertions.assertEquals(0, filteredInvokers3.size());
@@ -177,14 +177,14 @@ public class ConditionStateRouterTest {
         StateRouter router4 = new 
ConditionStateRouterFactory().getRouter(String.class, getRouteUrl(
                 "host = " + LOCAL_HOST + " & methods = getFoo => " + " host = 
10.20.3.3").addParameter(
                 FORCE_KEY, String.valueOf(true)));
-        List<Invoker<String>> filteredInvokers1 = router4.route(invokers,
+        List<Invoker<String>> filteredInvokers1 = 
router4.route(invokers.clone(),
                 URL.valueOf("consumer://" + LOCAL_HOST + 
"/com.foo.BarService"), invocation, false, new Holder<>());
         Assertions.assertEquals(1, filteredInvokers1.size());
 
         StateRouter router5 = new 
ConditionStateRouterFactory().getRouter(String.class, getRouteUrl(
                 "host = " + LOCAL_HOST + " & methods = unvalidmethod => " + " 
host = 10.20.3.3")
                 .addParameter(FORCE_KEY, String.valueOf(true)));
-        List<Invoker<String>> filteredInvokers2 = router5.route(invokers,
+        List<Invoker<String>> filteredInvokers2 = 
router5.route(invokers.clone(),
                 URL.valueOf("consumer://" + LOCAL_HOST + 
"/com.foo.BarService"), invocation, false, new Holder<>());
         Assertions.assertEquals(3, filteredInvokers2.size());
         // Request a non-exists method
@@ -199,7 +199,7 @@ public class ConditionStateRouterTest {
         originInvokers.add(new MockInvoker<String>());
         BitList<Invoker<String>> invokers = new BitList<>(originInvokers);
 
-        List<Invoker<String>> filteredInvokers = router.route(invokers, 
URL.valueOf("consumer://" + LOCAL_HOST + "/com.foo.BarService"), new 
RpcInvocation(), false, new Holder<>());
+        List<Invoker<String>> filteredInvokers = 
router.route(invokers.clone(), URL.valueOf("consumer://" + LOCAL_HOST + 
"/com.foo.BarService"), new RpcInvocation(), false, new Holder<>());
         Assertions.assertEquals(0, filteredInvokers.size());
     }
 
@@ -212,7 +212,7 @@ public class ConditionStateRouterTest {
         originInvokers.add(new MockInvoker<String>());
         BitList<Invoker<String>> invokers = new BitList<>(originInvokers);
 
-        List<Invoker<String>> filteredInvokers = router.route(invokers, 
URL.valueOf("consumer://" + LOCAL_HOST + "/com.foo.BarService"), new 
RpcInvocation(), false, new Holder<>());
+        List<Invoker<String>> filteredInvokers = 
router.route(invokers.clone(), URL.valueOf("consumer://" + LOCAL_HOST + 
"/com.foo.BarService"), new RpcInvocation(), false, new Holder<>());
         Assertions.assertEquals(0, filteredInvokers.size());
     }
 
@@ -225,7 +225,7 @@ public class ConditionStateRouterTest {
         originInvokers.add(new MockInvoker<String>(URL.valueOf("dubbo://" + 
LOCAL_HOST + ":20880/com.foo.BarService")));
         BitList<Invoker<String>> invokers = new BitList<>(originInvokers);
 
-        List<Invoker<String>> filteredInvokers = router.route(invokers, 
URL.valueOf("consumer://" + LOCAL_HOST + "/com.foo.BarService"), new 
RpcInvocation(), false, new Holder<>());
+        List<Invoker<String>> filteredInvokers = 
router.route(invokers.clone(), URL.valueOf("consumer://" + LOCAL_HOST + 
"/com.foo.BarService"), new RpcInvocation(), false, new Holder<>());
         Assertions.assertEquals(invokers, filteredInvokers);
     }
 
@@ -241,7 +241,7 @@ public class ConditionStateRouterTest {
         originInvokers.add(invoker3);
         BitList<Invoker<String>> invokers = new BitList<>(originInvokers);
 
-        List<Invoker<String>> filteredInvokers = router.route(invokers, 
URL.valueOf("consumer://" + LOCAL_HOST + "/com.foo.BarService"), new 
RpcInvocation(), false, new Holder<>());
+        List<Invoker<String>> filteredInvokers = 
router.route(invokers.clone(), URL.valueOf("consumer://" + LOCAL_HOST + 
"/com.foo.BarService"), new RpcInvocation(), false, new Holder<>());
         Assertions.assertEquals(2, filteredInvokers.size());
         Assertions.assertEquals(invoker2, filteredInvokers.get(0));
         Assertions.assertEquals(invoker3, filteredInvokers.get(1));
@@ -259,7 +259,7 @@ public class ConditionStateRouterTest {
         originInvokers.add(invoker3);
         BitList<Invoker<String>> invokers = new BitList<>(originInvokers);
 
-        List<Invoker<String>> filteredInvokers = router.route(invokers, 
URL.valueOf("consumer://" + LOCAL_HOST + "/com.foo.BarService"), new 
RpcInvocation(), false, new Holder<>());
+        List<Invoker<String>> filteredInvokers = 
router.route(invokers.clone(), URL.valueOf("consumer://" + LOCAL_HOST + 
"/com.foo.BarService"), new RpcInvocation(), false, new Holder<>());
         Assertions.assertEquals(2, filteredInvokers.size());
         Assertions.assertEquals(invoker2, filteredInvokers.get(0));
         Assertions.assertEquals(invoker3, filteredInvokers.get(1));
@@ -277,7 +277,7 @@ public class ConditionStateRouterTest {
         originInvokers.add(invoker3);
         BitList<Invoker<String>> invokers = new BitList<>(originInvokers);
 
-        List<Invoker<String>> filteredInvokers = router.route(invokers, 
URL.valueOf("consumer://" + LOCAL_HOST + "/com.foo.BarService"), new 
RpcInvocation(), false, new Holder<>());
+        List<Invoker<String>> filteredInvokers = 
router.route(invokers.clone(), URL.valueOf("consumer://" + LOCAL_HOST + 
"/com.foo.BarService"), new RpcInvocation(), false, new Holder<>());
         Assertions.assertEquals(2, filteredInvokers.size());
         Assertions.assertEquals(invoker2, filteredInvokers.get(0));
         Assertions.assertEquals(invoker3, filteredInvokers.get(1));
@@ -295,7 +295,7 @@ public class ConditionStateRouterTest {
         originInvokers.add(invoker3);
         BitList<Invoker<String>> invokers = new BitList<>(originInvokers);
 
-        List<Invoker<String>> filteredInvokers = router.route(invokers, 
URL.valueOf("consumer://" + LOCAL_HOST + "/com.foo.BarService"), new 
RpcInvocation(), false, new Holder<>());
+        List<Invoker<String>> filteredInvokers = 
router.route(invokers.clone(), URL.valueOf("consumer://" + LOCAL_HOST + 
"/com.foo.BarService"), new RpcInvocation(), false, new Holder<>());
         Assertions.assertEquals(2, filteredInvokers.size());
         Assertions.assertEquals(invoker2, filteredInvokers.get(0));
         Assertions.assertEquals(invoker3, filteredInvokers.get(1));
@@ -313,7 +313,7 @@ public class ConditionStateRouterTest {
         originInvokers.add(invoker3);
         BitList<Invoker<String>> invokers = new BitList<>(originInvokers);
 
-        List<Invoker<String>> filteredInvokers = router.route(invokers, 
URL.valueOf("consumer://" + LOCAL_HOST + "/com.foo.BarService"), new 
RpcInvocation(), false, new Holder<>());
+        List<Invoker<String>> filteredInvokers = 
router.route(invokers.clone(), URL.valueOf("consumer://" + LOCAL_HOST + 
"/com.foo.BarService"), new RpcInvocation(), false, new Holder<>());
         Assertions.assertEquals(invokers, filteredInvokers);
     }
 
@@ -329,7 +329,7 @@ public class ConditionStateRouterTest {
         originInvokers.add(invoker3);
         BitList<Invoker<String>> invokers = new BitList<>(originInvokers);
 
-        List<Invoker<String>> filteredInvokers = router.route(invokers, 
URL.valueOf("consumer://" + LOCAL_HOST + "/com.foo.BarService"), new 
RpcInvocation(), false, new Holder<>());
+        List<Invoker<String>> filteredInvokers = 
router.route(invokers.clone(), URL.valueOf("consumer://" + LOCAL_HOST + 
"/com.foo.BarService"), new RpcInvocation(), false, new Holder<>());
         Assertions.assertEquals(0, filteredInvokers.size());
     }
 
@@ -348,25 +348,25 @@ public class ConditionStateRouterTest {
         RpcInvocation invocation = new RpcInvocation();
         String p = "a";
         invocation.setArguments(new Object[]{null});
-        List<Invoker<String>> fileredInvokers = router.route(invokers, 
URL.valueOf("consumer://" + LOCAL_HOST + "/com.foo.BarService"), invocation, 
false, new Holder<>());
+        List<Invoker<String>> fileredInvokers = router.route(invokers.clone(), 
URL.valueOf("consumer://" + LOCAL_HOST + "/com.foo.BarService"), invocation, 
false, new Holder<>());
         Assertions.assertEquals(3, fileredInvokers.size());
 
         invocation.setArguments(new Object[]{p});
-        fileredInvokers = router.route(invokers, URL.valueOf("consumer://" + 
LOCAL_HOST + "/com.foo.BarService"), invocation, false, new Holder<>());
+        fileredInvokers = router.route(invokers.clone(), 
URL.valueOf("consumer://" + LOCAL_HOST + "/com.foo.BarService"), invocation, 
false, new Holder<>());
         Assertions.assertEquals(0, fileredInvokers.size());
 
         router = new ConditionStateRouterFactory().getRouter(String.class, 
getRouteUrl("arguments = b " + " => " + " host = 
1.2.3.4").addParameter(FORCE_KEY, String.valueOf(true)));
-        fileredInvokers = router.route(invokers, URL.valueOf("consumer://" + 
LOCAL_HOST + "/com.foo.BarService"), invocation, false, new Holder<>());
+        fileredInvokers = router.route(invokers.clone(), 
URL.valueOf("consumer://" + LOCAL_HOST + "/com.foo.BarService"), invocation, 
false, new Holder<>());
         Assertions.assertEquals(3, fileredInvokers.size());
 
         router = new ConditionStateRouterFactory().getRouter(String.class, 
getRouteUrl("arguments[10].inner = a " + " => " + " host = 
1.2.3.4").addParameter(FORCE_KEY, String.valueOf(true)));
-        fileredInvokers = router.route(invokers, URL.valueOf("consumer://" + 
LOCAL_HOST + "/com.foo.BarService"), invocation, false, new Holder<>());
+        fileredInvokers = router.route(invokers.clone(), 
URL.valueOf("consumer://" + LOCAL_HOST + "/com.foo.BarService"), invocation, 
false, new Holder<>());
         Assertions.assertEquals(3, fileredInvokers.size());
 
         int integer = 1;
         invocation.setArguments(new Object[]{integer});
         router = new ConditionStateRouterFactory().getRouter(String.class, 
getRouteUrl("arguments[0].inner = 1 " + " => " + " host = 
1.2.3.4").addParameter(FORCE_KEY, String.valueOf(true)));
-        fileredInvokers = router.route(invokers, URL.valueOf("consumer://" + 
LOCAL_HOST + "/com.foo.BarService"), invocation, false, new Holder<>());
+        fileredInvokers = router.route(invokers.clone(), 
URL.valueOf("consumer://" + LOCAL_HOST + "/com.foo.BarService"), invocation, 
false, new Holder<>());
         Assertions.assertEquals(0, fileredInvokers.size());
     }
 }
diff --git 
a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/file/FileRouterEngineTest.java
 
b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/file/FileRouterEngineTest.java
index 6cfc1e3a25..7c0d2d0781 100644
--- 
a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/file/FileRouterEngineTest.java
+++ 
b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/file/FileRouterEngineTest.java
@@ -172,7 +172,7 @@ public class FileRouterEngineTest {
         URL dicInitUrl = 
URL.valueOf("consumer://localhost:20880/org.apache.dubbo.rpc.cluster.router.file.FileRouterEngineTest?application=FileRouterEngineTest");
         dic = new StaticDirectory<>(dicInitUrl, invokers);
         dic.buildRouterChain();
-        
dic.getRouterChain().setHeadStateRouter(routerFactory.getRouter(FileRouterEngineTest.class,
 url));
+        
dic.getRouterChain().getCurrentChain().setHeadStateRouter(routerFactory.getRouter(FileRouterEngineTest.class,
 url));
     }
 
     static class MockClusterInvoker<T> extends AbstractClusterInvoker<T> {
diff --git 
a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/script/ScriptStateRouterTest.java
 
b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/script/ScriptStateRouterTest.java
index 8be79f9db7..13a9a03880 100644
--- 
a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/script/ScriptStateRouterTest.java
+++ 
b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/script/ScriptStateRouterTest.java
@@ -63,7 +63,7 @@ public class ScriptStateRouterTest {
         originInvokers.add(new MockInvoker<String>());
         BitList<Invoker<String>> invokers = new BitList<>(originInvokers);
 
-        List<Invoker<String>> filteredInvokers = router.route(invokers, 
invokers.get(0).getUrl(), new RpcInvocation(), false, new Holder<>());
+        List<Invoker<String>> filteredInvokers = 
router.route(invokers.clone(), invokers.get(0).getUrl(), new RpcInvocation(), 
false, new Holder<>());
         Assertions.assertEquals(invokers, filteredInvokers);
     }
 
@@ -88,7 +88,7 @@ public class ScriptStateRouterTest {
         originInvokers.add(invoker3);
         BitList<Invoker<String>> invokers = new BitList<>(originInvokers);
 
-        List<Invoker<String>> filteredInvokers = router.route(invokers, 
invokers.get(0).getUrl(), new RpcInvocation(), false, new Holder<>());
+        List<Invoker<String>> filteredInvokers = 
router.route(invokers.clone(), invokers.get(0).getUrl(), new RpcInvocation(), 
false, new Holder<>());
         Assertions.assertEquals(2, filteredInvokers.size());
         Assertions.assertEquals(invoker2, filteredInvokers.get(0));
         Assertions.assertEquals(invoker3, filteredInvokers.get(1));
@@ -119,7 +119,7 @@ public class ScriptStateRouterTest {
             "route(invokers, invocation, context) ";
 
         StateRouter router = new 
ScriptStateRouterFactory().getRouter(String.class, getRouteUrl(script));
-        List<Invoker<String>> routeResult = router.route(invokers, 
invokers.get(0).getUrl(), new RpcInvocation(), false, new Holder<>());
+        List<Invoker<String>> routeResult = router.route(invokers.clone(), 
invokers.get(0).getUrl(), new RpcInvocation(), false, new Holder<>());
         Assertions.assertEquals(1, routeResult.size());
         Assertions.assertEquals(invoker2, routeResult.get(0));
     }
@@ -137,7 +137,7 @@ public class ScriptStateRouterTest {
 
         String script = "/";
         StateRouter router = new 
ScriptStateRouterFactory().getRouter(String.class, getRouteUrl(script));
-        List<Invoker<String>> routeResult = router.route(invokers, 
invokers.get(0).getUrl(), new RpcInvocation(), false, new Holder<>());
+        List<Invoker<String>> routeResult = router.route(invokers.clone(), 
invokers.get(0).getUrl(), new RpcInvocation(), false, new Holder<>());
         Assertions.assertEquals(3, routeResult.size());
     }
 }
diff --git 
a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/tag/TagStateRouterTest.java
 
b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/tag/TagStateRouterTest.java
index 18c14319a4..0604240166 100644
--- 
a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/tag/TagStateRouterTest.java
+++ 
b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/tag/TagStateRouterTest.java
@@ -32,6 +32,7 @@ import 
org.apache.dubbo.rpc.cluster.router.tag.model.TagRouterRule;
 import org.apache.dubbo.rpc.cluster.router.tag.model.TagRuleParser;
 import org.apache.dubbo.rpc.model.ApplicationModel;
 import org.apache.dubbo.rpc.model.ModuleModel;
+
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
@@ -88,7 +89,7 @@ public class TagStateRouterTest {
 
         RpcInvocation invocation = new RpcInvocation();
         invocation.setAttachment(TAG_KEY, "tag2");
-        List<Invoker<String>> filteredInvokers = router.route(invokers, 
invokers.get(0).getUrl(), invocation, false, new Holder<>());
+        List<Invoker<String>> filteredInvokers = 
router.route(invokers.clone(), invokers.get(0).getUrl(), invocation, false, new 
Holder<>());
         Assertions.assertEquals(1, filteredInvokers.size());
         Assertions.assertEquals(invoker1, filteredInvokers.get(0));
     }
diff --git 
a/dubbo-common/src/main/java/org/apache/dubbo/common/constants/LoggerCodeConstants.java
 
b/dubbo-common/src/main/java/org/apache/dubbo/common/constants/LoggerCodeConstants.java
index 5dc83f19dd..78304d3c93 100644
--- 
a/dubbo-common/src/main/java/org/apache/dubbo/common/constants/LoggerCodeConstants.java
+++ 
b/dubbo-common/src/main/java/org/apache/dubbo/common/constants/LoggerCodeConstants.java
@@ -71,6 +71,8 @@ public interface LoggerCodeConstants {
 
     String REGISTRY_MISSING_METADATA_CONFIG_PORT = "1-18";
 
+    String REGISTRY_ROUTER_WAIT_LONG = "1-19";
+
     // cluster module 2-1 ~ 2-18
     String CLUSTER_FAILED_SITE_SELECTION = "2-1";
 
@@ -166,4 +168,7 @@ public interface LoggerCodeConstants {
 
     String INTERNAL_SERVICE_CONFIG_ERROR = "6-3";
 
+    String INTERNAL_ERROR = "99-0";
+
+    String INTERNAL_INTERRUPTED = "99-1";
 }


Reply via email to