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

xiaoyu 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 1746a8e21 [type:feat] add L1 cache and trie as L2 cache (#4417)
1746a8e21 is described below

commit 1746a8e21d9686a055dc408424829438d32d87af
Author: moremind <[email protected]>
AuthorDate: Thu Mar 2 11:36:39 2023 +0800

    [type:feat] add L1 cache and trie as L2 cache (#4417)
    
    * [type:feat] add L1 cache and trie as L2 cache
    
    * [type:feat] add L1 cache and trie as L2 cache
    
    * fix npe
    
    * fix npe
---
 .../src/main/resources/application.yml             |  10 +-
 .../shenyu/common/cache/WindowTinyLFUMap.java      |  75 +++++++++++++++
 .../apache/shenyu/common/config/ShenyuConfig.java  | 105 +++++++++++++++++++--
 .../shenyu/plugin/base/AbstractShenyuPlugin.java   |  98 ++++++++++++++-----
 .../base/cache/CommonPluginDataSubscriber.java     |   5 +-
 .../shenyu/plugin/base/cache/MatchDataCache.java   |  48 ++++++++++
 .../plugin/base/AbstractShenyuPluginTest.java      |  20 ++--
 .../web/controller/LocalPluginController.java      |   2 +-
 8 files changed, 319 insertions(+), 44 deletions(-)

diff --git a/shenyu-bootstrap/src/main/resources/application.yml 
b/shenyu-bootstrap/src/main/resources/application.yml
index f90973d6f..f0cf675d0 100644
--- a/shenyu-bootstrap/src/main/resources/application.yml
+++ b/shenyu-bootstrap/src/main/resources/application.yml
@@ -66,14 +66,18 @@ management:
       enabled: false
 
 shenyu:
+  matchCache:
+    selector:
+      selectorEnabled: false
+      maxSelectorFreeMemory: 256 # 256MB
+    rule:
+      initialCapacity: 10000 # initial capacity in cache
+      maximumSize: 10000 # max size in cache
   trie:
     childrenSize: 10000
     pathVariableSize: 1000
     pathRuleCacheSize: 1000
     matchMode: antPathMatch
-  matchCache:
-    selectorEnabled: false
-    maxSelectorFreeMemory: 256 # 256MB
   netty:
     http:
       # set to false, user can custom the netty tcp server config.
diff --git 
a/shenyu-common/src/main/java/org/apache/shenyu/common/cache/WindowTinyLFUMap.java
 
b/shenyu-common/src/main/java/org/apache/shenyu/common/cache/WindowTinyLFUMap.java
new file mode 100644
index 000000000..27114aaf4
--- /dev/null
+++ 
b/shenyu-common/src/main/java/org/apache/shenyu/common/cache/WindowTinyLFUMap.java
@@ -0,0 +1,75 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.shenyu.common.cache;
+
+import com.github.benmanes.caffeine.cache.Cache;
+import com.github.benmanes.caffeine.cache.Caffeine;
+
+import javax.annotation.concurrent.ThreadSafe;
+import java.io.Serializable;
+import java.util.AbstractMap;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+
+@ThreadSafe
+public class WindowTinyLFUMap<K, V> extends AbstractMap<K, V> implements 
Serializable {
+    
+    private static final long serialVersionUID = 2176631265536166614L;
+    
+    private final Cache<K, V> cache;
+    
+    public WindowTinyLFUMap(final int initialCapacity, final long maximumSize) 
{
+        this.cache = Caffeine.newBuilder()
+                .initialCapacity(initialCapacity)
+                .maximumSize(maximumSize)
+                .build();
+    }
+    
+    public WindowTinyLFUMap(final int initialSize, final long 
expireAfterWrite, final long maximumSize) {
+        this.cache = Caffeine.newBuilder()
+                .initialCapacity(initialSize)
+                .expireAfterWrite(expireAfterWrite, TimeUnit.MILLISECONDS)
+                .maximumSize(maximumSize)
+                .build();
+    }
+    
+    @Override
+    public V put(final K key, final V value) {
+        V v = cache.getIfPresent(value);
+        cache.put(key, value);
+        return v;
+    }
+    
+    @Override
+    public V get(final Object key) {
+        return cache.getIfPresent(key);
+    }
+    
+    @Override
+    public V remove(final Object key) {
+        V value = cache.getIfPresent(key);
+        cache.invalidate(key);
+        cache.cleanUp();
+        return value;
+    }
+    
+    @Override
+    public Set<Entry<K, V>> entrySet() {
+        return cache.asMap().entrySet();
+    }
+}
diff --git 
a/shenyu-common/src/main/java/org/apache/shenyu/common/config/ShenyuConfig.java 
b/shenyu-common/src/main/java/org/apache/shenyu/common/config/ShenyuConfig.java
index 3f1bd8b68..e47509e05 100644
--- 
a/shenyu-common/src/main/java/org/apache/shenyu/common/config/ShenyuConfig.java
+++ 
b/shenyu-common/src/main/java/org/apache/shenyu/common/config/ShenyuConfig.java
@@ -531,14 +531,60 @@ public class ShenyuConfig {
      * the match cache.
      */
     public static class MatchCache {
-
+        
+        private SelectorCacheConfig selector = new SelectorCacheConfig();
+        
+        private RuleCacheConfig rule = new RuleCacheConfig();
+    
+        /**
+         * get selector cache config.
+         *
+         * @return {@linkplain SelectorCacheConfig}
+         */
+        public SelectorCacheConfig getSelector() {
+            return selector;
+        }
+    
+        /**
+         * set selector cache config.
+         *
+         * @param selector SelectorCacheConfig
+         */
+        public void setSelector(final SelectorCacheConfig selector) {
+            this.selector = selector;
+        }
+    
+        /**
+         * get rule cache Config.
+         *
+         * @return rule cache config
+         */
+        public RuleCacheConfig getRule() {
+            return rule;
+        }
+    
+        /**
+         * set rule cache config.
+         *
+         * @param rule rule cache
+         */
+        public void setRule(final RuleCacheConfig rule) {
+            this.rule = rule;
+        }
+    }
+    
+    /**
+     * selector cache.
+     */
+    public static class SelectorCacheConfig {
+        
         private boolean selectorEnabled;
-
+    
         /**
          * Max free memory, unit mb.
          */
         private Integer maxSelectorFreeMemory = 256;
-
+    
         /**
          * Get selector cache enabled.
          *
@@ -547,7 +593,7 @@ public class ShenyuConfig {
         public boolean getSelectorEnabled() {
             return selectorEnabled;
         }
-
+    
         /**
          * Set selector enabled.
          *
@@ -556,8 +602,7 @@ public class ShenyuConfig {
         public void setSelectorEnabled(final boolean selectorEnabled) {
             this.selectorEnabled = selectorEnabled;
         }
-
-
+        
         /**
          * Gets maxFreeMemory.
          *
@@ -566,7 +611,7 @@ public class ShenyuConfig {
         public Integer getMaxSelectorFreeMemory() {
             return maxSelectorFreeMemory;
         }
-
+    
         /**
          * Sets maxFreeMemory.
          *
@@ -577,6 +622,52 @@ public class ShenyuConfig {
         }
     }
     
+    /**
+     * rule cache config.
+     */
+    public static class RuleCacheConfig {
+
+        private int initialCapacity = 10000;
+        
+        private long maximumSize = 10000L;
+    
+        /**
+         * get initial capacity.
+         *
+         * @return initial capacity
+         */
+        public int getInitialCapacity() {
+            return initialCapacity;
+        }
+        
+        /**
+         * set initial capacity.
+         *
+         * @param initialCapacity initialCapacity
+         */
+        public void setInitialCapacity(final int initialCapacity) {
+            this.initialCapacity = initialCapacity;
+        }
+    
+        /**
+         * get maximum size.
+         *
+         * @return rule cache maximumSize
+         */
+        public long getMaximumSize() {
+            return maximumSize;
+        }
+    
+        /**
+         * set rule cache maximumSize.
+         *
+         * @param maximumSize rule cache maximumSize
+         */
+        public void setMaximumSize(final long maximumSize) {
+            this.maximumSize = maximumSize;
+        }
+    }
+    
     /**
      * The type Exclude path.
      */
diff --git 
a/shenyu-plugin/shenyu-plugin-base/src/main/java/org/apache/shenyu/plugin/base/AbstractShenyuPlugin.java
 
b/shenyu-plugin/shenyu-plugin-base/src/main/java/org/apache/shenyu/plugin/base/AbstractShenyuPlugin.java
index 8d84659ab..1e9e627a1 100644
--- 
a/shenyu-plugin/shenyu-plugin-base/src/main/java/org/apache/shenyu/plugin/base/AbstractShenyuPlugin.java
+++ 
b/shenyu-plugin/shenyu-plugin-base/src/main/java/org/apache/shenyu/plugin/base/AbstractShenyuPlugin.java
@@ -57,6 +57,8 @@ public abstract class AbstractShenyuPlugin implements 
ShenyuPlugin {
     private static final String URI_CONDITION_TYPE = "uri";
 
     private ShenyuConfig.MatchCache matchCacheConfig;
+    
+    private ShenyuTrie trie;
 
     /**
      * this is Template Method child has Implement your own logic.
@@ -79,7 +81,7 @@ public abstract class AbstractShenyuPlugin implements 
ShenyuPlugin {
      */
     @Override
     public Mono<Void> execute(final ServerWebExchange exchange, final 
ShenyuPluginChain chain) {
-        initMatchCacheConfig();
+        initCacheConfig();
         final String pluginName = named();
         PluginData pluginData = 
BaseDataCache.getInstance().obtainPluginData(pluginName);
         // early exit
@@ -100,14 +102,14 @@ public abstract class AbstractShenyuPlugin implements 
ShenyuPlugin {
             Pair<Boolean, SelectorData> matchSelectorData = 
matchSelector(exchange, selectors);
             selectorData = matchSelectorData.getRight();
             if (Objects.isNull(selectorData)) {
-                if (matchCacheConfig.getSelectorEnabled() && 
matchSelectorData.getLeft()) {
+                if (matchCacheConfig.getSelector().getSelectorEnabled() && 
matchSelectorData.getLeft()) {
                     selectorData = new SelectorData();
                     selectorData.setPluginName(pluginName);
                     cacheSelectorData(path, selectorData);
                 }
                 return handleSelectorIfNull(pluginName, exchange, chain);
             } else {
-                if (matchCacheConfig.getSelectorEnabled() && 
matchSelectorData.getLeft()) {
+                if (matchCacheConfig.getSelector().getSelectorEnabled() && 
matchSelectorData.getLeft()) {
                     cacheSelectorData(path, selectorData);
                 }
             }
@@ -121,24 +123,30 @@ public abstract class AbstractShenyuPlugin implements 
ShenyuPlugin {
         if (CollectionUtils.isEmpty(rules)) {
             return handleRuleIfNull(pluginName, exchange, chain);
         }
-        RuleData ruleData;
+        RuleData ruleData = obtainRuleDataCache(path);
+        if (Objects.nonNull(ruleData) && Objects.isNull(ruleData.getId())) {
+            return handleRuleIfNull(pluginName, exchange, chain);
+        }
         if (selectorData.getType() == SelectorTypeEnum.FULL_FLOW.getCode()) {
             //get last
             RuleData rule = rules.get(rules.size() - 1);
             printLog(rule, pluginName);
             return doExecute(exchange, chain, selectorData, rule);
         } else {
-            // match path with rule uri condition
-            ShenyuTrieNode matchTrieNode = 
SpringBeanUtils.getInstance().getBean(ShenyuTrie.class).match(path, 
selectorData.getId());
-            if (Objects.nonNull(matchTrieNode)) {
-                List<RuleData> ruleDataList = 
matchTrieNode.getPathRuleCache().getIfPresent(selectorData.getId());
-                if (CollectionUtils.isNotEmpty(ruleDataList)) {
-                    ruleData = genericMatchRule(exchange, ruleDataList);
-                } else {
-                    ruleData = genericMatchRule(exchange, rules);
+            // lru map as L1 cache,the cache is enabled by default.
+            // if the L1 cache fails to hit, using L2 cache based on trie 
cache.
+            // if the L2 cache fails to hit, execute default strategy.
+            if (Objects.isNull(ruleData)) {
+                // L1 cache not exist data, try to get data through trie cache
+                ruleData = trieMatchRule(exchange, selectorData, path);
+                // trie cache fails to hit, execute default strategy
+                if (Objects.isNull(ruleData)) {
+                    Pair<Boolean, RuleData> matchRuleData = 
matchRule(exchange, rules);
+                    ruleData = matchRuleData.getRight();
+                    if (matchRuleData.getLeft()) {
+                        cacheRuleData(path, ruleData);
+                    }
                 }
-            } else {
-                ruleData = genericMatchRule(exchange, rules);
             }
             if (Objects.isNull(ruleData)) {
                 return handleRuleIfNull(pluginName, exchange, chain);
@@ -148,13 +156,19 @@ public abstract class AbstractShenyuPlugin implements 
ShenyuPlugin {
         return doExecute(exchange, chain, selectorData, ruleData);
     }
 
-    private void initMatchCacheConfig() {
+    private void initCacheConfig() {
         if (Objects.isNull(matchCacheConfig)) {
             matchCacheConfig = 
SpringBeanUtils.getInstance().getBean(ShenyuConfig.class).getMatchCache();
         }
+        if (Objects.isNull(trie)) {
+            trie = SpringBeanUtils.getInstance().getBean(ShenyuTrie.class);
+        }
     }
 
     private void cacheSelectorData(final String path, final SelectorData 
selectorData) {
+        if (Objects.isNull(selectorData)) {
+            return;
+        }
         if (StringUtils.isBlank(selectorData.getId())) {
             MatchDataCache.getInstance().cacheSelectorData(path, selectorData, 
getSelectorMaxFreeMemory());
             return;
@@ -169,11 +183,11 @@ public abstract class AbstractShenyuPlugin implements 
ShenyuPlugin {
     }
 
     private Integer getSelectorMaxFreeMemory() {
-        return matchCacheConfig.getMaxSelectorFreeMemory() * 1024 * 1024;
+        return matchCacheConfig.getSelector().getMaxSelectorFreeMemory() * 
1024 * 1024;
     }
 
     private SelectorData obtainSelectorDataCacheIfEnabled(final String path) {
-        return matchCacheConfig.getSelectorEnabled() ? 
MatchDataCache.getInstance().obtainSelectorData(named(), path) : null;
+        return matchCacheConfig.getSelector().getSelectorEnabled() ? 
MatchDataCache.getInstance().obtainSelectorData(named(), path) : null;
     }
 
     protected RuleData defaultRuleData(final SelectorData selectorData) {
@@ -230,21 +244,13 @@ public abstract class AbstractShenyuPlugin implements 
ShenyuPlugin {
         return true;
     }
 
-    private RuleData genericMatchRule(final ServerWebExchange exchange, final 
Collection<RuleData> rules) {
-        Pair<Boolean, RuleData> genericMatchRule = this.matchRule(exchange, 
rules);
-        if (genericMatchRule.getLeft()) {
-            return genericMatchRule.getRight();
-        }
-        return null;
-    }
-
     private Pair<Boolean, RuleData> matchRule(final ServerWebExchange 
exchange, final Collection<RuleData> rules) {
         List<RuleData> filterRuleData = rules.stream()
                 .filter(rule -> filterRule(rule, exchange))
                 .distinct()
                 .collect(Collectors.toList());
         if (filterRuleData.size() > 1) {
-            return Pair.of(Boolean.TRUE, manyMatchRule(filterRuleData));
+            return Pair.of(Boolean.FALSE, manyMatchRule(filterRuleData));
         } else {
             return Pair.of(Boolean.TRUE, 
filterRuleData.stream().findFirst().orElse(null));
         }
@@ -268,6 +274,46 @@ public abstract class AbstractShenyuPlugin implements 
ShenyuPlugin {
     private Boolean filterRule(final RuleData ruleData, final 
ServerWebExchange exchange) {
         return ruleData.getEnabled() && 
MatchStrategyFactory.match(ruleData.getMatchMode(), 
ruleData.getConditionDataList(), exchange);
     }
+    
+    private RuleData obtainRuleDataCache(final String path) {
+        return MatchDataCache.getInstance().obtainRuleData(named(), path);
+    }
+    
+    private void cacheRuleData(final String path, final RuleData ruleData) {
+        if (Objects.isNull(ruleData)) {
+            return;
+        }
+        int initialCapacity = matchCacheConfig.getRule().getInitialCapacity();
+        long maximumSize = matchCacheConfig.getRule().getMaximumSize();
+        if (StringUtils.isBlank(ruleData.getId())) {
+            MatchDataCache.getInstance().cacheRuleData(path, ruleData, 
initialCapacity, maximumSize);
+            return;
+        }
+        List<ConditionData> conditionList = ruleData.getConditionDataList();
+        if (CollectionUtils.isNotEmpty(conditionList)) {
+            boolean isUriCondition = conditionList.stream().allMatch(v -> 
URI_CONDITION_TYPE.equals(v.getParamType()));
+            if (isUriCondition) {
+                MatchDataCache.getInstance().cacheRuleData(path, ruleData, 
initialCapacity, maximumSize);
+            }
+        }
+    }
+    
+    private RuleData trieMatchRule(final ServerWebExchange exchange, final 
SelectorData selectorData, final String path) {
+        RuleData ruleData = null;
+        ShenyuTrieNode shenyuTrieNode = trie.match(path, selectorData.getId());
+        if (Objects.nonNull(shenyuTrieNode)) {
+            List<RuleData> ruleDataList = 
shenyuTrieNode.getPathRuleCache().getIfPresent(selectorData.getId());
+            if (CollectionUtils.isNotEmpty(ruleDataList)) {
+                Pair<Boolean, RuleData> ruleDataPair = matchRule(exchange, 
ruleDataList);
+                ruleData = ruleDataPair.getRight();
+                if (ruleDataPair.getLeft()) {
+                    // exist only one rule data, cache rule
+                    cacheRuleData(path, ruleData);
+                }
+            }
+        }
+        return ruleData;
+    }
 
     private void printLog(final Object data, final String pluginName) {
         if (data instanceof SelectorData) {
diff --git 
a/shenyu-plugin/shenyu-plugin-base/src/main/java/org/apache/shenyu/plugin/base/cache/CommonPluginDataSubscriber.java
 
b/shenyu-plugin/shenyu-plugin-base/src/main/java/org/apache/shenyu/plugin/base/cache/CommonPluginDataSubscriber.java
index b14f9d0b9..5174271bb 100644
--- 
a/shenyu-plugin/shenyu-plugin-base/src/main/java/org/apache/shenyu/plugin/base/cache/CommonPluginDataSubscriber.java
+++ 
b/shenyu-plugin/shenyu-plugin-base/src/main/java/org/apache/shenyu/plugin/base/cache/CommonPluginDataSubscriber.java
@@ -151,6 +151,7 @@ public class CommonPluginDataSubscriber implements 
PluginDataSubscriber {
     @Override
     public void refreshRuleDataAll() {
         BaseDataCache.getInstance().cleanRuleData();
+        MatchDataCache.getInstance().cleanRuleDataData();
         SpringBeanUtils.getInstance().getBean(ShenyuTrie.class).clear();
     }
     
@@ -205,10 +206,11 @@ public class CommonPluginDataSubscriber implements 
PluginDataSubscriber {
             BaseDataCache.getInstance().cacheRuleData(ruleData);
             Optional.ofNullable(handlerMap.get(ruleData.getPluginName()))
                     .ifPresent(handler -> handler.handlerRule(ruleData));
+            
MatchDataCache.getInstance().removeRuleData(ruleData.getPluginName());
             if 
(CollectionUtils.isEmpty(ruleData.getBeforeConditionDataList())) {
                 eventPublisher.publishEvent(new 
RuleTrieEvent(RuleTrieEventEnum.INSERT, ruleData));
             } else {
-                // if has before condition, use upodate
+                // if rule data has before condition, update trie
                 eventPublisher.publishEvent(new 
RuleTrieEvent(RuleTrieEventEnum.UPDATE, ruleData));
             }
         }
@@ -255,6 +257,7 @@ public class CommonPluginDataSubscriber implements 
PluginDataSubscriber {
             BaseDataCache.getInstance().removeRuleData(ruleData);
             Optional.ofNullable(handlerMap.get(ruleData.getPluginName()))
                     .ifPresent(handler -> handler.removeRule(ruleData));
+            
MatchDataCache.getInstance().removeRuleData(ruleData.getPluginName());
             eventPublisher.publishEvent(new 
RuleTrieEvent(RuleTrieEventEnum.REMOVE, ruleData));
         }
     }
diff --git 
a/shenyu-plugin/shenyu-plugin-base/src/main/java/org/apache/shenyu/plugin/base/cache/MatchDataCache.java
 
b/shenyu-plugin/shenyu-plugin-base/src/main/java/org/apache/shenyu/plugin/base/cache/MatchDataCache.java
index eafb2face..879e2ef5f 100644
--- 
a/shenyu-plugin/shenyu-plugin-base/src/main/java/org/apache/shenyu/plugin/base/cache/MatchDataCache.java
+++ 
b/shenyu-plugin/shenyu-plugin-base/src/main/java/org/apache/shenyu/plugin/base/cache/MatchDataCache.java
@@ -19,6 +19,8 @@ package org.apache.shenyu.plugin.base.cache;
 
 import com.google.common.collect.Maps;
 import org.apache.shenyu.common.cache.MemorySafeWindowTinyLFUMap;
+import org.apache.shenyu.common.cache.WindowTinyLFUMap;
+import org.apache.shenyu.common.dto.RuleData;
 import org.apache.shenyu.common.dto.SelectorData;
 import org.apache.shenyu.common.utils.MapUtils;
 
@@ -38,6 +40,12 @@ public final class MatchDataCache {
      * pluginName -> LRUMap.
      */
     private static final ConcurrentMap<String, Map<String, SelectorData>> 
SELECTOR_DATA_MAP = Maps.newConcurrentMap();
+    
+    /**
+     * plugin name -> LRU Map.
+     * LRU Map: path -> rule data.
+     */
+    private static final ConcurrentMap<String, Map<String, RuleData>> 
RULE_DATA_MAP = Maps.newConcurrentMap();
 
     private MatchDataCache() {
     }
@@ -89,4 +97,44 @@ public final class MatchDataCache {
         final Map<String, SelectorData> lruMap = 
SELECTOR_DATA_MAP.get(pluginName);
         return Optional.ofNullable(lruMap).orElse(Maps.newHashMap()).get(path);
     }
+    
+    /**
+     * cache rule data.
+     *
+     * @param path path
+     * @param ruleData rule data
+     * @param initialCapacity initial capacity
+     * @param maximumSize maximum size
+     */
+    public void cacheRuleData(final String path, final RuleData ruleData, 
final int initialCapacity, final long maximumSize) {
+        MapUtils.computeIfAbsent(RULE_DATA_MAP, ruleData.getPluginName(), map 
-> new WindowTinyLFUMap<>(initialCapacity, maximumSize)).put(path, ruleData);
+    }
+    
+    /**
+     * remove rule data.
+     *
+     * @param pluginName pluginName
+     */
+    public void removeRuleData(final String pluginName) {
+        RULE_DATA_MAP.remove(pluginName);
+    }
+    
+    /**
+     * clear the cache.
+     */
+    public void cleanRuleDataData() {
+        RULE_DATA_MAP.clear();
+    }
+    
+    /**
+     * get rule data.
+     *
+     * @param pluginName pluginName
+     * @param path path
+     * @return ruleData
+     */
+    public RuleData obtainRuleData(final String pluginName, final String path) 
{
+        final Map<String, RuleData> lruMap = RULE_DATA_MAP.get(pluginName);
+        return Optional.ofNullable(lruMap).orElse(Maps.newHashMap()).get(path);
+    }
 }
diff --git 
a/shenyu-plugin/shenyu-plugin-base/src/test/java/org/apache/shenyu/plugin/base/AbstractShenyuPluginTest.java
 
b/shenyu-plugin/shenyu-plugin-base/src/test/java/org/apache/shenyu/plugin/base/AbstractShenyuPluginTest.java
index b00e5729d..0ea30a92b 100644
--- 
a/shenyu-plugin/shenyu-plugin-base/src/test/java/org/apache/shenyu/plugin/base/AbstractShenyuPluginTest.java
+++ 
b/shenyu-plugin/shenyu-plugin-base/src/test/java/org/apache/shenyu/plugin/base/AbstractShenyuPluginTest.java
@@ -67,18 +67,26 @@ public final class AbstractShenyuPluginTest {
     @BeforeEach
     public void setUp() {
         mockShenyuConfig();
-        this.ruleData = RuleData.builder().id("1")
-                .selectorId("1").enabled(true)
-                .loged(true).sort(1).build();
+        this.ruleData = RuleData.builder()
+                .id("1")
+                .pluginName("SHENYU")
+                .selectorId("1")
+                .enabled(true)
+                .loged(true)
+                .sort(1).build();
         this.conditionData = new ConditionData();
         this.conditionData.setOperator("match");
         this.conditionData.setParamName("/");
         this.conditionData.setParamType("uri");
         this.conditionData.setParamValue("/http/**");
         this.shenyuPluginChain = mock(ShenyuPluginChain.class);
-        this.pluginData = 
PluginData.builder().name("SHENYU").enabled(true).build();
-        this.selectorData = SelectorData.builder().id("1").pluginName("SHENYU")
-                
.enabled(true).type(SelectorTypeEnum.CUSTOM_FLOW.getCode()).build();
+        this.pluginData = PluginData.builder()
+                .name("SHENYU")
+                .enabled(true).build();
+        this.selectorData = SelectorData.builder()
+                .id("1").pluginName("SHENYU")
+                .enabled(true)
+                .type(SelectorTypeEnum.CUSTOM_FLOW.getCode()).build();
         this.testShenyuPlugin = spy(new TestShenyuPlugin());
         this.exchange = 
MockServerWebExchange.from(MockServerHttpRequest.get("/http/SHENYU/SHENYU")
                 .build());
diff --git 
a/shenyu-web/src/main/java/org/apache/shenyu/web/controller/LocalPluginController.java
 
b/shenyu-web/src/main/java/org/apache/shenyu/web/controller/LocalPluginController.java
index 5c823a172..4fc8f0668 100644
--- 
a/shenyu-web/src/main/java/org/apache/shenyu/web/controller/LocalPluginController.java
+++ 
b/shenyu-web/src/main/java/org/apache/shenyu/web/controller/LocalPluginController.java
@@ -98,9 +98,9 @@ public class LocalPluginController {
         final List<String> selectorIds = 
selectorData.stream().map(SelectorData::getId).collect(Collectors.toList());
         BaseDataCache.getInstance().removeSelectDataByPluginName(name);
         MatchDataCache.getInstance().removeSelectorData(name);
+        MatchDataCache.getInstance().removeRuleData(name);
         for (String selectorId : selectorIds) {
             BaseDataCache.getInstance().removeRuleDataBySelectorId(selectorId);
-            
             List<RuleData> ruleDataList = 
BaseDataCache.getInstance().obtainRuleData(selectorId);
             if (CollectionUtils.isNotEmpty(ruleDataList)) {
                 ruleDataList.forEach(rule -> {

Reply via email to