This is an automated email from the ASF dual-hosted git repository. dmsolr pushed a commit to branch fix/refresh-stale-config-guard-6874 in repository https://gitbox.apache.org/repos/asf/shenyu.git
commit bf89fa194483989e86a3da1c697a5df9b0a4ea3e Author: Luke Haochao Zhuang <[email protected]> AuthorDate: Mon Aug 31 15:40:59 2026 +0800 fix: remove faulty size guard so REFRESH cleans up stale plugin/auth/meta config nodes (#6874) The REFRESH/MYSELF branch of AbstractNodeDataChangedListener#onCommonChanged only removed stale entries when the old set strictly outnumbered the new one, so refreshes with equal or larger cardinality (e.g. old=[A,B,C,D] -> new=[C,D,E,F]) left orphaned config nodes in nacos/apollo/polaris. Removed the size guard and fixed the stale-entry delConfig call to use the correct namespaced key. --- .../listener/AbstractNodeDataChangedListener.java | 4 ++-- .../AbstractNodeDataChangedListenerTest.java | 25 ++++++++++++++++++++++ 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/shenyu-admin-listener/shenyu-admin-listener-api/src/main/java/org/apache/shenyu/admin/listener/AbstractNodeDataChangedListener.java b/shenyu-admin-listener/shenyu-admin-listener-api/src/main/java/org/apache/shenyu/admin/listener/AbstractNodeDataChangedListener.java index 21e6b6d594..5b1fc6741c 100644 --- a/shenyu-admin-listener/shenyu-admin-listener-api/src/main/java/org/apache/shenyu/admin/listener/AbstractNodeDataChangedListener.java +++ b/shenyu-admin-listener/shenyu-admin-listener-api/src/main/java/org/apache/shenyu/admin/listener/AbstractNodeDataChangedListener.java @@ -131,9 +131,9 @@ public abstract class AbstractNodeDataChangedListener implements DataChangedList final List<String> configDataNames = this.getConfigDataNames(configKeyPrefix); changedList.forEach(changedData -> publishConfig(configKeyPrefix + mapperToKey.apply(changedData), changedData)); - if (Objects.nonNull(configDataNames) && configDataNames.size() > changedList.size()) { + if (Objects.nonNull(configDataNames)) { configDataNames.removeAll(changeNames); - configDataNames.forEach(this::delConfig); + configDataNames.forEach(staleName -> delConfig(configKeyPrefix + staleName)); } publishConfig(configKeyPrefix + DefaultNodeConstants.LIST_STR, changeNames); diff --git a/shenyu-admin-listener/shenyu-admin-listener-api/src/test/java/org/apache/shenyu/admin/listener/AbstractNodeDataChangedListenerTest.java b/shenyu-admin-listener/shenyu-admin-listener-api/src/test/java/org/apache/shenyu/admin/listener/AbstractNodeDataChangedListenerTest.java index f552617407..a1a9cbeff9 100644 --- a/shenyu-admin-listener/shenyu-admin-listener-api/src/test/java/org/apache/shenyu/admin/listener/AbstractNodeDataChangedListenerTest.java +++ b/shenyu-admin-listener/shenyu-admin-listener-api/src/test/java/org/apache/shenyu/admin/listener/AbstractNodeDataChangedListenerTest.java @@ -18,6 +18,7 @@ package org.apache.shenyu.admin.listener; import org.apache.shenyu.common.constant.DefaultNodeConstants; +import org.apache.shenyu.common.dto.PluginData; import org.apache.shenyu.common.dto.SelectorData; import org.apache.shenyu.common.enums.DataEventTypeEnum; import org.apache.shenyu.common.utils.GsonUtils; @@ -69,6 +70,30 @@ public final class AbstractNodeDataChangedListenerTest { assertTrue(listener.wasDeleted(selectorDataKey(SECOND_PLUGIN, STALE_ID))); } + @Test + public void testOnPluginChangedRefreshWithEqualCardinalityRemovesStaleEntries() { + TestNodeDataChangedListener listener = new TestNodeDataChangedListener(); + final String configKeyPrefix = NAMESPACE_ID + DefaultNodeConstants.JOIN_POINT + "plugin" + DefaultNodeConstants.JOIN_POINT; + listener.putConfig(configKeyPrefix + DefaultNodeConstants.LIST_STR, Arrays.asList("A", "B", "C", "D")); + listener.putConfig(configKeyPrefix + "A", pluginData("A")); + listener.putConfig(configKeyPrefix + "B", pluginData("B")); + + listener.onPluginChanged(Arrays.asList( + pluginData("C"), pluginData("D"), pluginData("E"), pluginData("F")), DataEventTypeEnum.REFRESH); + + assertNull(listener.config(configKeyPrefix + "A")); + assertNull(listener.config(configKeyPrefix + "B")); + assertTrue(listener.wasDeleted(configKeyPrefix + "A")); + assertTrue(listener.wasDeleted(configKeyPrefix + "B")); + } + + private static PluginData pluginData(final String name) { + return PluginData.builder() + .namespaceId(NAMESPACE_ID) + .name(name) + .build(); + } + private static SelectorData selectorData(final String pluginName, final String selectorId) { return SelectorData.builder() .namespaceId(NAMESPACE_ID)
