- use AtomicLong to replace AtomicInteger Signed-off-by: Eric Zhao <sczyh16@gmail.com>master
@@ -121,7 +121,7 @@ public final class ParamFlowChecker { | |||||
static boolean passDefaultLocalCheck(ResourceWrapper resourceWrapper, ParamFlowRule rule, int acquireCount, | static boolean passDefaultLocalCheck(ResourceWrapper resourceWrapper, ParamFlowRule rule, int acquireCount, | ||||
Object value) { | Object value) { | ||||
ParameterMetric metric = getParameterMetric(resourceWrapper); | ParameterMetric metric = getParameterMetric(resourceWrapper); | ||||
CacheMap<Object, AtomicInteger> tokenCounters = metric == null ? null : metric.getRuleTokenCounter(rule); | |||||
CacheMap<Object, AtomicLong> tokenCounters = metric == null ? null : metric.getRuleTokenCounter(rule); | |||||
CacheMap<Object, AtomicLong> timeCounters = metric == null ? null : metric.getRuleTimeCounter(rule); | CacheMap<Object, AtomicLong> timeCounters = metric == null ? null : metric.getRuleTimeCounter(rule); | ||||
if (tokenCounters == null || timeCounters == null) { | if (tokenCounters == null || timeCounters == null) { | ||||
@@ -130,7 +130,7 @@ public final class ParamFlowChecker { | |||||
// Calculate max token count (threshold) | // Calculate max token count (threshold) | ||||
Set<Object> exclusionItems = rule.getParsedHotItems().keySet(); | Set<Object> exclusionItems = rule.getParsedHotItems().keySet(); | ||||
int tokenCount = (int)rule.getCount(); | |||||
long tokenCount = (long)rule.getCount(); | |||||
if (exclusionItems.contains(value)) { | if (exclusionItems.contains(value)) { | ||||
tokenCount = rule.getParsedHotItems().get(value); | tokenCount = rule.getParsedHotItems().get(value); | ||||
} | } | ||||
@@ -139,7 +139,7 @@ public final class ParamFlowChecker { | |||||
return false; | return false; | ||||
} | } | ||||
int maxCount = tokenCount + rule.getBurstCount(); | |||||
long maxCount = tokenCount + rule.getBurstCount(); | |||||
if (acquireCount > maxCount) { | if (acquireCount > maxCount) { | ||||
return false; | return false; | ||||
} | } | ||||
@@ -150,7 +150,7 @@ public final class ParamFlowChecker { | |||||
AtomicLong lastAddTokenTime = timeCounters.putIfAbsent(value, new AtomicLong(currentTime)); | AtomicLong lastAddTokenTime = timeCounters.putIfAbsent(value, new AtomicLong(currentTime)); | ||||
if (lastAddTokenTime == null) { | if (lastAddTokenTime == null) { | ||||
// Token never added, just replenish the tokens and consume {@code acquireCount} immediately. | // Token never added, just replenish the tokens and consume {@code acquireCount} immediately. | ||||
tokenCounters.putIfAbsent(value, new AtomicInteger(maxCount - acquireCount)); | |||||
tokenCounters.putIfAbsent(value, new AtomicLong(maxCount - acquireCount)); | |||||
return true; | return true; | ||||
} | } | ||||
@@ -158,15 +158,15 @@ public final class ParamFlowChecker { | |||||
long passTime = currentTime - lastAddTokenTime.get(); | long passTime = currentTime - lastAddTokenTime.get(); | ||||
// A simplified token bucket algorithm that will replenish the tokens only when statistic window has passed. | // A simplified token bucket algorithm that will replenish the tokens only when statistic window has passed. | ||||
if (passTime > rule.getDurationInSec() * 1000) { | if (passTime > rule.getDurationInSec() * 1000) { | ||||
AtomicInteger oldQps = tokenCounters.putIfAbsent(value, new AtomicInteger(maxCount - acquireCount)); | |||||
AtomicLong oldQps = tokenCounters.putIfAbsent(value, new AtomicLong(maxCount - acquireCount)); | |||||
if (oldQps == null) { | if (oldQps == null) { | ||||
// Might not be accurate here. | // Might not be accurate here. | ||||
lastAddTokenTime.set(currentTime); | lastAddTokenTime.set(currentTime); | ||||
return true; | return true; | ||||
} else { | } else { | ||||
int restQps = oldQps.get(); | |||||
int toAddCount = (int)((passTime * tokenCount) / (rule.getDurationInSec() * 1000)); | |||||
int newQps = (restQps + toAddCount) > maxCount ? (maxCount - acquireCount) | |||||
long restQps = oldQps.get(); | |||||
long toAddCount = (passTime * tokenCount) / (rule.getDurationInSec() * 1000); | |||||
long newQps = toAddCount + restQps > maxCount ? (maxCount - acquireCount) | |||||
: (restQps + toAddCount - acquireCount); | : (restQps + toAddCount - acquireCount); | ||||
if (newQps < 0) { | if (newQps < 0) { | ||||
@@ -179,9 +179,9 @@ public final class ParamFlowChecker { | |||||
Thread.yield(); | Thread.yield(); | ||||
} | } | ||||
} else { | } else { | ||||
AtomicInteger oldQps = tokenCounters.get(value); | |||||
AtomicLong oldQps = tokenCounters.get(value); | |||||
if (oldQps != null) { | if (oldQps != null) { | ||||
int oldQpsValue = oldQps.get(); | |||||
long oldQpsValue = oldQps.get(); | |||||
if (oldQpsValue - acquireCount >= 0) { | if (oldQpsValue - acquireCount >= 0) { | ||||
if (oldQps.compareAndSet(oldQpsValue, oldQpsValue - acquireCount)) { | if (oldQps.compareAndSet(oldQpsValue, oldQpsValue - acquireCount)) { | ||||
return true; | return true; | ||||
@@ -51,7 +51,7 @@ public class ParameterMetric { | |||||
* | * | ||||
* @since 1.6.0 | * @since 1.6.0 | ||||
*/ | */ | ||||
private final Map<ParamFlowRule, CacheMap<Object, AtomicInteger>> ruleTokenCounter = new HashMap<>(); | |||||
private final Map<ParamFlowRule, CacheMap<Object, AtomicLong>> ruleTokenCounter = new HashMap<>(); | |||||
private final Map<Integer, CacheMap<Object, AtomicInteger>> threadCountMap = new HashMap<>(); | private final Map<Integer, CacheMap<Object, AtomicInteger>> threadCountMap = new HashMap<>(); | ||||
/** | /** | ||||
@@ -61,7 +61,7 @@ public class ParameterMetric { | |||||
* @return the associated token counter | * @return the associated token counter | ||||
* @since 1.6.0 | * @since 1.6.0 | ||||
*/ | */ | ||||
public CacheMap<Object, AtomicInteger> getRuleTokenCounter(ParamFlowRule rule) { | |||||
public CacheMap<Object, AtomicLong> getRuleTokenCounter(ParamFlowRule rule) { | |||||
return ruleTokenCounter.get(rule); | return ruleTokenCounter.get(rule); | ||||
} | } | ||||
@@ -98,7 +98,7 @@ public class ParameterMetric { | |||||
synchronized (lock) { | synchronized (lock) { | ||||
if (ruleTokenCounter.get(rule) == null) { | if (ruleTokenCounter.get(rule) == null) { | ||||
long size = Math.min(BASE_PARAM_MAX_CAPACITY * rule.getDurationInSec(), TOTAL_MAX_CAPACITY); | long size = Math.min(BASE_PARAM_MAX_CAPACITY * rule.getDurationInSec(), TOTAL_MAX_CAPACITY); | ||||
ruleTokenCounter.put(rule, new ConcurrentLinkedHashMapWrapper<Object, AtomicInteger>(size)); | |||||
ruleTokenCounter.put(rule, new ConcurrentLinkedHashMapWrapper<Object, AtomicLong>(size)); | |||||
} | } | ||||
} | } | ||||
} | } | ||||
@@ -245,7 +245,7 @@ public class ParameterMetric { | |||||
* | * | ||||
* @return the token counter map | * @return the token counter map | ||||
*/ | */ | ||||
Map<ParamFlowRule, CacheMap<Object, AtomicInteger>> getRuleTokenCounterMap() { | |||||
Map<ParamFlowRule, CacheMap<Object, AtomicLong>> getRuleTokenCounterMap() { | |||||
return ruleTokenCounter; | return ruleTokenCounter; | ||||
} | } | ||||
@@ -160,7 +160,7 @@ public class ParamFlowCheckerTest { | |||||
ParameterMetric metric = new ParameterMetric(); | ParameterMetric metric = new ParameterMetric(); | ||||
ParameterMetricStorage.getMetricsMap().put(resourceWrapper.getName(), metric); | ParameterMetricStorage.getMetricsMap().put(resourceWrapper.getName(), metric); | ||||
metric.getRuleTimeCounterMap().put(rule, new ConcurrentLinkedHashMapWrapper<Object, AtomicLong>(4000)); | metric.getRuleTimeCounterMap().put(rule, new ConcurrentLinkedHashMapWrapper<Object, AtomicLong>(4000)); | ||||
metric.getRuleTokenCounterMap().put(rule, new ConcurrentLinkedHashMapWrapper<Object, AtomicInteger>(4000)); | |||||
metric.getRuleTokenCounterMap().put(rule, new ConcurrentLinkedHashMapWrapper<Object, AtomicLong>(4000)); | |||||
assertTrue(ParamFlowChecker.passCheck(resourceWrapper, rule, 1, list)); | assertTrue(ParamFlowChecker.passCheck(resourceWrapper, rule, 1, list)); | ||||
assertFalse(ParamFlowChecker.passCheck(resourceWrapper, rule, 1, list)); | assertFalse(ParamFlowChecker.passCheck(resourceWrapper, rule, 1, list)); | ||||
@@ -38,9 +38,49 @@ import com.alibaba.csp.sentinel.util.TimeUtil; | |||||
/** | /** | ||||
* @author jialiang.linjl | * @author jialiang.linjl | ||||
* @author Eric Zhao | |||||
*/ | */ | ||||
public class ParamFlowDefaultCheckerTest extends AbstractTimeBasedTest { | public class ParamFlowDefaultCheckerTest extends AbstractTimeBasedTest { | ||||
@Test | |||||
public void testCheckQpsWithLongIntervalAndHighThreshold() { | |||||
// This test case is intended to avoid number overflow. | |||||
final String resourceName = "testCheckQpsWithLongIntervalAndHighThreshold"; | |||||
final ResourceWrapper resourceWrapper = new StringResourceWrapper(resourceName, EntryType.IN); | |||||
int paramIdx = 0; | |||||
// Set a large threshold. | |||||
long threshold = 25000L; | |||||
ParamFlowRule rule = new ParamFlowRule(resourceName) | |||||
.setCount(threshold) | |||||
.setParamIdx(paramIdx); | |||||
String valueA = "valueA"; | |||||
ParameterMetric metric = new ParameterMetric(); | |||||
ParameterMetricStorage.getMetricsMap().put(resourceWrapper.getName(), metric); | |||||
metric.getRuleTimeCounterMap().put(rule, new ConcurrentLinkedHashMapWrapper<Object, AtomicLong>(4000)); | |||||
metric.getRuleTokenCounterMap().put(rule, | |||||
new ConcurrentLinkedHashMapWrapper<Object, AtomicLong>(4000)); | |||||
// We mock the time directly to avoid unstable behaviour. | |||||
setCurrentMillis(System.currentTimeMillis()); | |||||
assertTrue(ParamFlowChecker.passSingleValueCheck(resourceWrapper, rule, 1, valueA)); | |||||
assertTrue(ParamFlowChecker.passSingleValueCheck(resourceWrapper, rule, 1, valueA)); | |||||
// 24 hours passed. | |||||
// This can make `toAddCount` larger that Integer.MAX_VALUE. | |||||
sleep(1000 * 60 * 60 * 24); | |||||
assertTrue(ParamFlowChecker.passSingleValueCheck(resourceWrapper, rule, 1, valueA)); | |||||
assertTrue(ParamFlowChecker.passSingleValueCheck(resourceWrapper, rule, 1, valueA)); | |||||
// 48 hours passed. | |||||
sleep(1000 * 60 * 60 * 48); | |||||
assertTrue(ParamFlowChecker.passSingleValueCheck(resourceWrapper, rule, 1, valueA)); | |||||
assertTrue(ParamFlowChecker.passSingleValueCheck(resourceWrapper, rule, 1, valueA)); | |||||
} | |||||
@Test | @Test | ||||
public void testParamFlowDefaultCheckSingleQps() { | public void testParamFlowDefaultCheckSingleQps() { | ||||
final String resourceName = "testParamFlowDefaultCheckSingleQps"; | final String resourceName = "testParamFlowDefaultCheckSingleQps"; | ||||
@@ -59,7 +99,7 @@ public class ParamFlowDefaultCheckerTest extends AbstractTimeBasedTest { | |||||
ParameterMetricStorage.getMetricsMap().put(resourceWrapper.getName(), metric); | ParameterMetricStorage.getMetricsMap().put(resourceWrapper.getName(), metric); | ||||
metric.getRuleTimeCounterMap().put(rule, new ConcurrentLinkedHashMapWrapper<Object, AtomicLong>(4000)); | metric.getRuleTimeCounterMap().put(rule, new ConcurrentLinkedHashMapWrapper<Object, AtomicLong>(4000)); | ||||
metric.getRuleTokenCounterMap().put(rule, | metric.getRuleTokenCounterMap().put(rule, | ||||
new ConcurrentLinkedHashMapWrapper<Object, AtomicInteger>(4000)); | |||||
new ConcurrentLinkedHashMapWrapper<Object, AtomicLong>(4000)); | |||||
// We mock the time directly to avoid unstable behaviour. | // We mock the time directly to avoid unstable behaviour. | ||||
setCurrentMillis(System.currentTimeMillis()); | setCurrentMillis(System.currentTimeMillis()); | ||||
@@ -99,7 +139,7 @@ public class ParamFlowDefaultCheckerTest extends AbstractTimeBasedTest { | |||||
ParameterMetricStorage.getMetricsMap().put(resourceWrapper.getName(), metric); | ParameterMetricStorage.getMetricsMap().put(resourceWrapper.getName(), metric); | ||||
metric.getRuleTimeCounterMap().put(rule, new ConcurrentLinkedHashMapWrapper<Object, AtomicLong>(4000)); | metric.getRuleTimeCounterMap().put(rule, new ConcurrentLinkedHashMapWrapper<Object, AtomicLong>(4000)); | ||||
metric.getRuleTokenCounterMap().put(rule, | metric.getRuleTokenCounterMap().put(rule, | ||||
new ConcurrentLinkedHashMapWrapper<Object, AtomicInteger>(4000)); | |||||
new ConcurrentLinkedHashMapWrapper<Object, AtomicLong>(4000)); | |||||
// We mock the time directly to avoid unstable behaviour. | // We mock the time directly to avoid unstable behaviour. | ||||
setCurrentMillis(System.currentTimeMillis()); | setCurrentMillis(System.currentTimeMillis()); | ||||
@@ -169,7 +209,7 @@ public class ParamFlowDefaultCheckerTest extends AbstractTimeBasedTest { | |||||
ParameterMetricStorage.getMetricsMap().put(resourceWrapper.getName(), metric); | ParameterMetricStorage.getMetricsMap().put(resourceWrapper.getName(), metric); | ||||
metric.getRuleTimeCounterMap().put(rule, new ConcurrentLinkedHashMapWrapper<Object, AtomicLong>(4000)); | metric.getRuleTimeCounterMap().put(rule, new ConcurrentLinkedHashMapWrapper<Object, AtomicLong>(4000)); | ||||
metric.getRuleTokenCounterMap().put(rule, | metric.getRuleTokenCounterMap().put(rule, | ||||
new ConcurrentLinkedHashMapWrapper<Object, AtomicInteger>(4000)); | |||||
new ConcurrentLinkedHashMapWrapper<Object, AtomicLong>(4000)); | |||||
// We mock the time directly to avoid unstable behaviour. | // We mock the time directly to avoid unstable behaviour. | ||||
setCurrentMillis(System.currentTimeMillis()); | setCurrentMillis(System.currentTimeMillis()); | ||||
@@ -222,7 +262,7 @@ public class ParamFlowDefaultCheckerTest extends AbstractTimeBasedTest { | |||||
ParameterMetricStorage.getMetricsMap().put(resourceWrapper.getName(), metric); | ParameterMetricStorage.getMetricsMap().put(resourceWrapper.getName(), metric); | ||||
metric.getRuleTimeCounterMap().put(rule, new ConcurrentLinkedHashMapWrapper<Object, AtomicLong>(4000)); | metric.getRuleTimeCounterMap().put(rule, new ConcurrentLinkedHashMapWrapper<Object, AtomicLong>(4000)); | ||||
metric.getRuleTokenCounterMap().put(rule, | metric.getRuleTokenCounterMap().put(rule, | ||||
new ConcurrentLinkedHashMapWrapper<Object, AtomicInteger>(4000)); | |||||
new ConcurrentLinkedHashMapWrapper<Object, AtomicLong>(4000)); | |||||
int threadCount = 40; | int threadCount = 40; | ||||
final CountDownLatch waitLatch = new CountDownLatch(threadCount); | final CountDownLatch waitLatch = new CountDownLatch(threadCount); | ||||
@@ -99,8 +99,8 @@ public class ParamFlowSlotTest { | |||||
ParameterMetric metric = mock(ParameterMetric.class); | ParameterMetric metric = mock(ParameterMetric.class); | ||||
CacheMap<Object, AtomicLong> map = new ConcurrentLinkedHashMapWrapper<Object, AtomicLong>(4000); | |||||
CacheMap<Object, AtomicInteger> map2 = new ConcurrentLinkedHashMapWrapper<Object, AtomicInteger>(4000); | |||||
CacheMap<Object, AtomicLong> map = new ConcurrentLinkedHashMapWrapper<>(4000); | |||||
CacheMap<Object, AtomicLong> map2 = new ConcurrentLinkedHashMapWrapper<>(4000); | |||||
when(metric.getRuleTimeCounter(rule)).thenReturn(map); | when(metric.getRuleTimeCounter(rule)).thenReturn(map); | ||||
when(metric.getRuleTokenCounter(rule)).thenReturn(map2); | when(metric.getRuleTokenCounter(rule)).thenReturn(map2); | ||||
map.put(argToGo, new AtomicLong(TimeUtil.currentTimeMillis())); | map.put(argToGo, new AtomicLong(TimeUtil.currentTimeMillis())); | ||||