From f24614594775fe5a331ca7e039f06f908d0fe202 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=99=BD?= <575121043@qq.com> Date: Tue, 27 Oct 2026 17:15:25 +0800 Subject: [PATCH 1/2] Fix application script router after rule deletion --- .../script/config/AppScriptStateRouter.java | 29 +++++++++++++------ .../config/AppScriptStateRouterTest.java | 24 +++++++++++++++ 2 files changed, 44 insertions(+), 9 deletions(-) diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/script/config/AppScriptStateRouter.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/script/config/AppScriptStateRouter.java index e872a7ea3f3d..b4cb2881e716 100644 --- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/script/config/AppScriptStateRouter.java +++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/script/config/AppScriptStateRouter.java @@ -49,10 +49,19 @@ public class AppScriptStateRouter extends AbstractStateRouter implements C LoggerFactory.getErrorTypeAwareLogger(AppScriptStateRouter.class); private static final String RULE_SUFFIX = ".script-router"; - private ScriptRule scriptRule; - private ScriptStateRouter scriptRouter; + private volatile RouterState routerState; private String application; + private static final class RouterState { + private final ScriptRule rule; + private final ScriptStateRouter router; + + private RouterState(ScriptRule rule, ScriptStateRouter router) { + this.rule = rule; + this.router = router; + } + } + public AppScriptStateRouter(URL url) { super(url); } @@ -66,16 +75,17 @@ protected BitList> doRoute( Holder> routerSnapshotNodeHolder, Holder messageHolder) throws RpcException { - if (scriptRouter == null || !scriptRule.isValid() || !scriptRule.isEnabled()) { + RouterState state = routerState; + if (state == null || state.router == null || !state.rule.isValid() || !state.rule.isEnabled()) { if (needToPrintMessage) { messageHolder.set( "Directly return from script router. Reason: Invokers from previous router is empty or script is not enabled. Script rule is: " - + (scriptRule == null ? "null" : scriptRule.getRawRule())); + + (state == null ? "null" : state.rule.getRawRule())); } return invokers; } - invokers = scriptRouter.route(invokers, url, invocation, needToPrintMessage, routerSnapshotNodeHolder); + invokers = state.router.route(invokers, url, invocation, needToPrintMessage, routerSnapshotNodeHolder); if (needToPrintMessage) { messageHolder.set(messageHolder.get()); @@ -93,16 +103,16 @@ public synchronized void process(ConfigChangedEvent event) { try { if (event.getChangeType().equals(ConfigChangeType.DELETED)) { - this.scriptRule = null; + routerState = null; } else { - this.scriptRule = ScriptRule.parse(event.getContent()); + ScriptRule scriptRule = ScriptRule.parse(event.getContent()); URL scriptUrl = getUrl().addParameter( TYPE_KEY, isEmpty(scriptRule.getType()) ? DEFAULT_SCRIPT_TYPE_KEY : scriptRule.getType()) .addParameterAndEncoded(RULE_KEY, scriptRule.getScript()) .addParameter(FORCE_KEY, scriptRule.isForce()) .addParameter(RUNTIME_KEY, scriptRule.isRuntime()); - scriptRouter = new ScriptStateRouter<>(scriptUrl); + routerState = new RouterState<>(scriptRule, new ScriptStateRouter<>(scriptUrl)); } } catch (Exception e) { logger.error( @@ -160,6 +170,7 @@ public void stop() { // for testing purpose public void setScriptRule(ScriptRule scriptRule) { - this.scriptRule = scriptRule; + RouterState state = routerState; + routerState = scriptRule == null ? null : new RouterState<>(scriptRule, state == null ? null : state.router); } } diff --git a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/script/config/AppScriptStateRouterTest.java b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/script/config/AppScriptStateRouterTest.java index 36aba171cee9..2a5da8bd95c0 100644 --- a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/script/config/AppScriptStateRouterTest.java +++ b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/script/config/AppScriptStateRouterTest.java @@ -17,6 +17,8 @@ package org.apache.dubbo.rpc.cluster.router.script.config; import org.apache.dubbo.common.URL; +import org.apache.dubbo.common.config.configcenter.ConfigChangeType; +import org.apache.dubbo.common.config.configcenter.ConfigChangedEvent; import org.apache.dubbo.common.config.configcenter.DynamicConfiguration; import org.apache.dubbo.common.utils.Holder; import org.apache.dubbo.rpc.Invoker; @@ -82,6 +84,28 @@ void testConfigScriptRoute() { Assertions.assertEquals(1, result.size()); } + @Test + void deletingRulePassesThroughAndRepublishingRestoresRouting() { + AppScriptStateRouter router = new AppScriptStateRouter<>(url); + BitList> invokers = getInvokers(); + RpcInvocation invocation = new RpcInvocation(); + invocation.setMethodName("sayHello"); + String ruleKey = "demo-provider" + RULE_SUFFIX; + + router.process(new ConfigChangedEvent( + ruleKey, DynamicConfiguration.DEFAULT_GROUP, rawRule, ConfigChangeType.ADDED)); + Assertions.assertEquals(1, router.route(invokers.clone(), url, invocation, false, new Holder<>()).size()); + + router.process(new ConfigChangedEvent( + ruleKey, DynamicConfiguration.DEFAULT_GROUP, "", ConfigChangeType.DELETED)); + Assertions.assertEquals( + invokers.size(), router.route(invokers.clone(), url, invocation, false, new Holder<>()).size()); + + router.process(new ConfigChangedEvent( + ruleKey, DynamicConfiguration.DEFAULT_GROUP, rawRule, ConfigChangeType.ADDED)); + Assertions.assertEquals(1, router.route(invokers.clone(), url, invocation, false, new Holder<>()).size()); + } + private BitList> getInvokers() { List> originInvokers = new ArrayList>(); Invoker invoker1 = new MockInvoker( From 5156fdc3d3b5a73698530821721ebdbeab8652c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=99=BD?= <575121043@qq.com> Date: Tue, 27 Oct 2026 17:26:32 +0800 Subject: [PATCH 2/2] Format script router regression test --- .../config/AppScriptStateRouterTest.java | 26 ++++++++++++------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/script/config/AppScriptStateRouterTest.java b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/script/config/AppScriptStateRouterTest.java index 2a5da8bd95c0..15774f12f351 100644 --- a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/script/config/AppScriptStateRouterTest.java +++ b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/script/config/AppScriptStateRouterTest.java @@ -92,18 +92,26 @@ void deletingRulePassesThroughAndRepublishingRestoresRouting() { invocation.setMethodName("sayHello"); String ruleKey = "demo-provider" + RULE_SUFFIX; - router.process(new ConfigChangedEvent( - ruleKey, DynamicConfiguration.DEFAULT_GROUP, rawRule, ConfigChangeType.ADDED)); - Assertions.assertEquals(1, router.route(invokers.clone(), url, invocation, false, new Holder<>()).size()); + router.process( + new ConfigChangedEvent(ruleKey, DynamicConfiguration.DEFAULT_GROUP, rawRule, ConfigChangeType.ADDED)); + Assertions.assertEquals( + 1, + router.route(invokers.clone(), url, invocation, false, new Holder<>()) + .size()); - router.process(new ConfigChangedEvent( - ruleKey, DynamicConfiguration.DEFAULT_GROUP, "", ConfigChangeType.DELETED)); + router.process( + new ConfigChangedEvent(ruleKey, DynamicConfiguration.DEFAULT_GROUP, "", ConfigChangeType.DELETED)); Assertions.assertEquals( - invokers.size(), router.route(invokers.clone(), url, invocation, false, new Holder<>()).size()); + invokers.size(), + router.route(invokers.clone(), url, invocation, false, new Holder<>()) + .size()); - router.process(new ConfigChangedEvent( - ruleKey, DynamicConfiguration.DEFAULT_GROUP, rawRule, ConfigChangeType.ADDED)); - Assertions.assertEquals(1, router.route(invokers.clone(), url, invocation, false, new Holder<>()).size()); + router.process( + new ConfigChangedEvent(ruleKey, DynamicConfiguration.DEFAULT_GROUP, rawRule, ConfigChangeType.ADDED)); + Assertions.assertEquals( + 1, + router.route(invokers.clone(), url, invocation, false, new Holder<>()) + .size()); } private BitList> getInvokers() {