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/incubator-shenyu.git


The following commit(s) were added to refs/heads/master by this push:
     new dc3db1a3b Plugin modification concurrency issues #3620 (#3673)
dc3db1a3b is described below

commit dc3db1a3b9548d42151fa57d531a4932b62516d0
Author: hutaishi <[email protected]>
AuthorDate: Thu Jul 7 21:52:30 2022 +0800

    Plugin modification concurrency issues #3620 (#3673)
    
    * Plugin modification concurrency issues #3620
    
    * ShenyuWebHandler#putExtPlugins remove sync
    
    * ShenyuWebHandler#onPluginRemoved rollback
    
    * Plugin modification concurrency issues apache#3620
---
 .../shenyu/web/handler/ShenyuWebHandler.java       | 36 +++++++++++++---------
 1 file changed, 22 insertions(+), 14 deletions(-)

diff --git 
a/shenyu-web/src/main/java/org/apache/shenyu/web/handler/ShenyuWebHandler.java 
b/shenyu-web/src/main/java/org/apache/shenyu/web/handler/ShenyuWebHandler.java
index b7f20aa3c..3bdb0723b 100644
--- 
a/shenyu-web/src/main/java/org/apache/shenyu/web/handler/ShenyuWebHandler.java
+++ 
b/shenyu-web/src/main/java/org/apache/shenyu/web/handler/ShenyuWebHandler.java
@@ -41,7 +41,6 @@ import java.util.List;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Optional;
-import java.util.concurrent.CopyOnWriteArrayList;
 import java.util.stream.Collectors;
 
 /**
@@ -54,7 +53,7 @@ public final class ShenyuWebHandler implements WebHandler, 
ApplicationListener<P
     /**
      * this filed can not set to be final, because we should copyOnWrite to 
update plugins.
      */
-    private final List<ShenyuPlugin> plugins;
+    private volatile List<ShenyuPlugin> plugins;
 
     /**
      * source plugins, these plugins load from ShenyuPlugin, this filed can't 
change.
@@ -73,7 +72,7 @@ public final class ShenyuWebHandler implements WebHandler, 
ApplicationListener<P
      */
     public ShenyuWebHandler(final List<ShenyuPlugin> plugins, final 
ShenyuConfig shenyuConfig) {
         this.sourcePlugins = new ArrayList<>(plugins);
-        this.plugins = new CopyOnWriteArrayList<>(plugins);
+        this.plugins = new ArrayList<>(plugins);
         ShenyuConfig.Scheduler config = shenyuConfig.getScheduler();
         this.scheduled = config.getEnabled();
         if (scheduled) {
@@ -114,8 +113,10 @@ public final class ShenyuWebHandler implements WebHandler, 
ApplicationListener<P
                 .collect(Collectors.toList());
         if (CollectionUtils.isNotEmpty(shenyuPlugins)) {
             shenyuPlugins.forEach(plugin -> LOG.info("shenyu auto add extends 
plugins:{}", plugin.named()));
-            plugins.addAll(shenyuPlugins);
-            onSortedPlugins();
+            // copy new list
+            List<ShenyuPlugin> newPluginList = new ArrayList<>(plugins);
+            newPluginList.addAll(shenyuPlugins);
+            plugins = sortPlugins(newPluginList);
         }
     }
     
@@ -139,25 +140,26 @@ public final class ShenyuWebHandler implements 
WebHandler, ApplicationListener<P
                 break;
             case SORTED:
                 // copy a new one, or there will be concurrency problems
-                onSortedPlugins();
+                this.plugins = sortPlugins(new ArrayList<>(this.plugins));
                 break;
             default:
                 throw new IllegalStateException("Unexpected value: " + 
event.getPluginStateEnums());
         }
-        onSortedPlugins();
     }
 
     /**
      * sort plugins.
      *
+     * @param list list of plugin
      * @return sorted list
      */
-    private void onSortedPlugins() {
-        Map<String, Integer> pluginSortMap = 
this.plugins.stream().collect(Collectors.toMap(ShenyuPlugin::named, plugin -> {
+    private List<ShenyuPlugin> sortPlugins(final List<ShenyuPlugin> list) {
+        Map<String, Integer> pluginSortMap = 
list.stream().collect(Collectors.toMap(ShenyuPlugin::named, plugin -> {
             PluginData pluginData = 
BaseDataCache.getInstance().obtainPluginData(plugin.named());
             return 
Optional.ofNullable(pluginData).map(PluginData::getSort).orElse(plugin.getOrder());
         }));
-        this.plugins.sort(Comparator.comparingLong(plugin -> 
pluginSortMap.get(plugin.named())));
+        list.sort(Comparator.comparingLong(plugin -> 
pluginSortMap.get(plugin.named())));
+        return list;
     }
 
     /**
@@ -165,20 +167,26 @@ public final class ShenyuWebHandler implements 
WebHandler, ApplicationListener<P
      * @param pluginData plugin data
      * @return enabled plugins
      */
-    private void onPluginEnabled(final PluginData pluginData) {
+    private synchronized void onPluginEnabled(final PluginData pluginData) {
         LOG.info("shenyu use plugin:[{}]", pluginData.getName());
         final List<ShenyuPlugin> enabledPlugins = 
this.sourcePlugins.stream().filter(plugin -> 
plugin.named().equals(pluginData.getName())
                 && pluginData.getEnabled()).collect(Collectors.toList());
         enabledPlugins.removeAll(this.plugins);
-        this.plugins.addAll(enabledPlugins);
+        // copy a new plugin list.
+        List<ShenyuPlugin> newPluginList = new ArrayList<>(this.plugins);
+        newPluginList.addAll(enabledPlugins);
+        this.plugins = sortPlugins(newPluginList);
     }
 
     /**
      * handle removed or disabled plugin.
      * @param pluginData plugin data
      */
-    private void onPluginRemoved(final PluginData pluginData) {
-        this.plugins.removeIf(plugin -> 
plugin.named().equals(pluginData.getName()));
+    private synchronized void onPluginRemoved(final PluginData pluginData) {
+        // copy a new plugin list.
+        List<ShenyuPlugin> newPluginList = new ArrayList<>(this.plugins);
+        newPluginList.removeIf(plugin -> 
plugin.named().equals(pluginData.getName()));
+        this.plugins = newPluginList;
     }
 
     private static class DefaultShenyuPluginChain implements ShenyuPluginChain 
{

Reply via email to