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

Aias00 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 4a397c9c3c fix(sentinel): serialize global rule updates (#7125)
4a397c9c3c is described below

commit 4a397c9c3c3bfe37060e0af59c139180dafd992f
Author: Liming Deng <[email protected]>
AuthorDate: Tue Sep 22 09:12:50 2026 +0800

    fix(sentinel): serialize global rule updates (#7125)
---
 .../sentinel/handler/SentinelRuleHandle.java       | 82 ++++++++++++----------
 .../sentinel/handler/SentinelRuleHandleTest.java   | 68 +++++++++++++++---
 2 files changed, 101 insertions(+), 49 deletions(-)

diff --git 
a/shenyu-plugin/shenyu-plugin-fault-tolerance/shenyu-plugin-sentinel/src/main/java/org/apache/shenyu/plugin/sentinel/handler/SentinelRuleHandle.java
 
b/shenyu-plugin/shenyu-plugin-fault-tolerance/shenyu-plugin-sentinel/src/main/java/org/apache/shenyu/plugin/sentinel/handler/SentinelRuleHandle.java
index bd663488bd..cf2bdb0f9d 100644
--- 
a/shenyu-plugin/shenyu-plugin-fault-tolerance/shenyu-plugin-sentinel/src/main/java/org/apache/shenyu/plugin/sentinel/handler/SentinelRuleHandle.java
+++ 
b/shenyu-plugin/shenyu-plugin-fault-tolerance/shenyu-plugin-sentinel/src/main/java/org/apache/shenyu/plugin/sentinel/handler/SentinelRuleHandle.java
@@ -42,6 +42,8 @@ import java.util.stream.Collectors;
 public class SentinelRuleHandle implements PluginDataHandler {
     
     public static final Supplier<CommonHandleCache<String, SentinelHandle>> 
CACHED_HANDLE = new BeanHolder<>(CommonHandleCache::new);
+
+    private static final Object RULE_UPDATE_LOCK = new Object();
     
     @Override
     public void handlerSelector(final SelectorData selectorData) {
@@ -60,51 +62,55 @@ public class SentinelRuleHandle implements 
PluginDataHandler {
         SentinelHandle sentinelHandle = 
GsonUtils.getInstance().fromJson(ruleData.getHandle(), SentinelHandle.class);
         sentinelHandle.checkData();
         String key = CacheKeyUtils.INST.getKey(ruleData);
-        CACHED_HANDLE.get().cachedHandle(key, sentinelHandle);
-        List<FlowRule> flowRules = FlowRuleManager.getRules()
-                .stream()
-                .filter(r -> !r.getResource().equals(key))
-                .collect(Collectors.toList());
-        if (sentinelHandle.getFlowRuleEnable() == 
Constants.SENTINEL_ENABLE_FLOW_RULE) {
-            FlowRule rule = new FlowRule(key);
-            rule.setCount(sentinelHandle.getFlowRuleCount());
-            rule.setGrade(sentinelHandle.getFlowRuleGrade());
-            
rule.setControlBehavior(sentinelHandle.getFlowRuleControlBehavior());
-            
rule.setMaxQueueingTimeMs(sentinelHandle.getFlowRuleMaxQueueingTimeMs());
-            
rule.setWarmUpPeriodSec(sentinelHandle.getFlowRuleWarmUpPeriodSec());
-            flowRules.add(rule);
-        }
-        FlowRuleManager.loadRules(flowRules);
+        synchronized (RULE_UPDATE_LOCK) {
+            CACHED_HANDLE.get().cachedHandle(key, sentinelHandle);
+            List<FlowRule> flowRules = FlowRuleManager.getRules()
+                    .stream()
+                    .filter(r -> !r.getResource().equals(key))
+                    .collect(Collectors.toList());
+            if (sentinelHandle.getFlowRuleEnable() == 
Constants.SENTINEL_ENABLE_FLOW_RULE) {
+                FlowRule rule = new FlowRule(key);
+                rule.setCount(sentinelHandle.getFlowRuleCount());
+                rule.setGrade(sentinelHandle.getFlowRuleGrade());
+                
rule.setControlBehavior(sentinelHandle.getFlowRuleControlBehavior());
+                
rule.setMaxQueueingTimeMs(sentinelHandle.getFlowRuleMaxQueueingTimeMs());
+                
rule.setWarmUpPeriodSec(sentinelHandle.getFlowRuleWarmUpPeriodSec());
+                flowRules.add(rule);
+            }
+            FlowRuleManager.loadRules(flowRules);
 
-        List<DegradeRule> degradeRules = DegradeRuleManager.getRules()
-                .stream()
-                .filter(r -> !r.getResource().equals(key))
-                .collect(Collectors.toList());
-        if (sentinelHandle.getDegradeRuleEnable() == 
Constants.SENTINEL_ENABLE_DEGRADE_RULE) {
-            DegradeRule rule = new DegradeRule(key);
-            rule.setCount(sentinelHandle.getDegradeRuleCount());
-            rule.setGrade(sentinelHandle.getDegradeRuleGrade());
-            rule.setTimeWindow(sentinelHandle.getDegradeRuleTimeWindow());
-            
rule.setStatIntervalMs(sentinelHandle.getDegradeRuleStatIntervals() * 1000);
-            
rule.setMinRequestAmount(sentinelHandle.getDegradeRuleMinRequestAmount());
-            
rule.setSlowRatioThreshold(sentinelHandle.getDegradeRuleSlowRatioThreshold());
-            degradeRules.add(rule);
+            List<DegradeRule> degradeRules = DegradeRuleManager.getRules()
+                    .stream()
+                    .filter(r -> !r.getResource().equals(key))
+                    .collect(Collectors.toList());
+            if (sentinelHandle.getDegradeRuleEnable() == 
Constants.SENTINEL_ENABLE_DEGRADE_RULE) {
+                DegradeRule rule = new DegradeRule(key);
+                rule.setCount(sentinelHandle.getDegradeRuleCount());
+                rule.setGrade(sentinelHandle.getDegradeRuleGrade());
+                rule.setTimeWindow(sentinelHandle.getDegradeRuleTimeWindow());
+                
rule.setStatIntervalMs(sentinelHandle.getDegradeRuleStatIntervals() * 1000);
+                
rule.setMinRequestAmount(sentinelHandle.getDegradeRuleMinRequestAmount());
+                
rule.setSlowRatioThreshold(sentinelHandle.getDegradeRuleSlowRatioThreshold());
+                degradeRules.add(rule);
+            }
+            DegradeRuleManager.loadRules(degradeRules);
         }
-        DegradeRuleManager.loadRules(degradeRules);
     }
 
     @Override
     public void removeRule(final RuleData ruleData) {
         String key = CacheKeyUtils.INST.getKey(ruleData);
-        CACHED_HANDLE.get().removeHandle(key);
-        FlowRuleManager.loadRules(FlowRuleManager.getRules()
-                .stream()
-                .filter(r -> !r.getResource().equals(key))
-                .collect(Collectors.toList()));
-        DegradeRuleManager.loadRules(DegradeRuleManager.getRules()
-                .stream()
-                .filter(r -> !r.getResource().equals(key))
-                .collect(Collectors.toList()));
+        synchronized (RULE_UPDATE_LOCK) {
+            CACHED_HANDLE.get().removeHandle(key);
+            FlowRuleManager.loadRules(FlowRuleManager.getRules()
+                    .stream()
+                    .filter(r -> !r.getResource().equals(key))
+                    .collect(Collectors.toList()));
+            DegradeRuleManager.loadRules(DegradeRuleManager.getRules()
+                    .stream()
+                    .filter(r -> !r.getResource().equals(key))
+                    .collect(Collectors.toList()));
+        }
     }
 
     @Override
diff --git 
a/shenyu-plugin/shenyu-plugin-fault-tolerance/shenyu-plugin-sentinel/src/test/java/org/apache/shenyu/plugin/sentinel/handler/SentinelRuleHandleTest.java
 
b/shenyu-plugin/shenyu-plugin-fault-tolerance/shenyu-plugin-sentinel/src/test/java/org/apache/shenyu/plugin/sentinel/handler/SentinelRuleHandleTest.java
index a92aacc4af..ca152fc009 100644
--- 
a/shenyu-plugin/shenyu-plugin-fault-tolerance/shenyu-plugin-sentinel/src/test/java/org/apache/shenyu/plugin/sentinel/handler/SentinelRuleHandleTest.java
+++ 
b/shenyu-plugin/shenyu-plugin-fault-tolerance/shenyu-plugin-sentinel/src/test/java/org/apache/shenyu/plugin/sentinel/handler/SentinelRuleHandleTest.java
@@ -30,6 +30,15 @@ import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.ExtendWith;
 import org.mockito.junit.jupiter.MockitoExtension;
 
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.is;
 import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -42,6 +51,8 @@ public final class SentinelRuleHandleTest {
 
     @BeforeEach
     public void setUp() {
+        FlowRuleManager.loadRules(Collections.emptyList());
+        DegradeRuleManager.loadRules(Collections.emptyList());
         sentinelRuleHandle = new SentinelRuleHandle();
     }
 
@@ -52,9 +63,53 @@ public final class SentinelRuleHandleTest {
 
     @Test
     public void removeRule() {
+        RuleData data = createRuleData("removeRule");
+        sentinelRuleHandle.handlerRule(data);
+        FlowRule flowRule = FlowRuleManager.getRules().get(0);
+        assertThat(flowRule.getCount(), is(10.0));
+        assertThat(flowRule.getResource(), is("sentinel_removeRule"));
+        DegradeRule degradeRule = DegradeRuleManager.getRules().get(0);
+        assertThat(degradeRule.getCount(), is(1.0));
+        assertThat(degradeRule.getResource(), is("sentinel_removeRule"));
+        sentinelRuleHandle.removeRule(data);
+        assertTrue(FlowRuleManager.getRules().isEmpty());
+        assertTrue(DegradeRuleManager.getRules().isEmpty());
+    }
+
+    @Test
+    public void handlerRuleShouldNotLoseConcurrentUpdates() throws Exception {
+        int ruleCount = 16;
+        ExecutorService executor = Executors.newFixedThreadPool(ruleCount);
+        CountDownLatch ready = new CountDownLatch(ruleCount);
+        CountDownLatch start = new CountDownLatch(1);
+        List<Future<?>> futures = new ArrayList<>();
+        try {
+            for (int i = 0; i < ruleCount; i++) {
+                RuleData ruleData = createRuleData("concurrent-" + i);
+                futures.add(executor.submit(() -> {
+                    ready.countDown();
+                    start.await();
+                    sentinelRuleHandle.handlerRule(ruleData);
+                    return null;
+                }));
+            }
+            assertTrue(ready.await(5, TimeUnit.SECONDS));
+            start.countDown();
+            for (Future<?> future : futures) {
+                future.get(10, TimeUnit.SECONDS);
+            }
+
+            assertEquals(ruleCount, FlowRuleManager.getRules().size());
+            assertEquals(ruleCount, DegradeRuleManager.getRules().size());
+        } finally {
+            executor.shutdownNow();
+        }
+    }
+
+    private RuleData createRuleData(final String ruleId) {
         RuleData data = new RuleData();
         data.setSelectorId("sentinel");
-        data.setId("removeRule");
+        data.setId(ruleId);
         SentinelHandle sentinelHandle = new SentinelHandle();
         sentinelHandle.setFlowRuleCount(10);
         sentinelHandle.setFlowRuleGrade(0);
@@ -66,15 +121,6 @@ public final class SentinelRuleHandleTest {
         sentinelHandle.setDegradeRuleStatIntervals(10);
         sentinelHandle.setDegradeRuleSlowRatioThreshold(0.5d);
         data.setHandle(GsonUtils.getGson().toJson(sentinelHandle));
-        sentinelRuleHandle.handlerRule(data);
-        FlowRule flowRule = FlowRuleManager.getRules().get(0);
-        assertThat(flowRule.getCount(), is(10.0));
-        assertThat(flowRule.getResource(), is("sentinel_removeRule"));
-        DegradeRule degradeRule = DegradeRuleManager.getRules().get(0);
-        assertThat(degradeRule.getCount(), is(1.0));
-        assertThat(degradeRule.getResource(), is("sentinel_removeRule"));
-        sentinelRuleHandle.removeRule(data);
-        assertTrue(FlowRuleManager.getRules().isEmpty());
-        assertTrue(DegradeRuleManager.getRules().isEmpty());
+        return data;
     }
 }

Reply via email to