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;
}
}