From a65d16083dffd56069c0694d0f5417454d518b22 Mon Sep 17 00:00:00 2001 From: Eric Zhao Date: Wed, 8 Aug 2018 19:36:42 +0800 Subject: [PATCH] Optimize for statistic data structures (#47) * Optimize for leap array - Fix a bug: old position is not cleaned when inserting into a new (empty) position - Reuse buckets for optimization - The strategy is now changed: deprecated buckets will not be reset until newer time triggered. LeapArray is responsible for filtering the deprecated buckets (e.g. in `list` or `values`) - Update test cases Signed-off-by: Eric Zhao --- .../slots/statistic/base/LeapArray.java | 23 +++++++++--- .../sentinel/slots/statistic/base/Window.java | 8 +++- .../slots/statistic/base/WindowWrap.java | 14 +++++++ .../statistic/metric/WindowLeapArray.java | 34 ++++++++--------- .../base/metric/WindowLeapArrayTest.java | 37 +++++++++++++++++-- 5 files changed, 86 insertions(+), 30 deletions(-) diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/statistic/base/LeapArray.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/statistic/base/LeapArray.java index 78f5d627..a80946af 100755 --- a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/statistic/base/LeapArray.java +++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/statistic/base/LeapArray.java @@ -24,6 +24,7 @@ import com.alibaba.csp.sentinel.util.TimeUtil; /** * @param type of data wrapper * @author jialiang.linjl + * @author Eric Zhao */ public abstract class LeapArray { @@ -45,6 +46,12 @@ public abstract class LeapArray { return currentWindow(TimeUtil.currentTimeMillis()); } + /** + * Get window at provided timestamp. + * + * @param time a valid timestamp + * @return the window at provided timestamp + */ abstract public WindowWrap currentWindow(long time); public WindowWrap getPreviousWindow(long time) { @@ -53,8 +60,8 @@ public abstract class LeapArray { time = time - windowLength; WindowWrap wrap = array.get(idx); - if (wrap == null) { - return wrap; + if (wrap == null || isWindowDeprecated(wrap)) { + return null; } if (wrap.windowStart() + windowLength < (time)) { @@ -73,23 +80,27 @@ public abstract class LeapArray { int idx = (int)(timeId % array.length()); WindowWrap old = array.get(idx); - if (old == null) { + if (old == null || isWindowDeprecated(old)) { return null; } return old.value(); } - public AtomicReferenceArray> array() { + AtomicReferenceArray> array() { return array; } + private boolean isWindowDeprecated(WindowWrap windowWrap) { + return TimeUtil.currentTimeMillis() - windowWrap.windowStart() >= intervalInMs; + } + public List> list() { ArrayList> result = new ArrayList>(); for (int i = 0; i < array.length(); i++) { WindowWrap windowWrap = array.get(i); - if (windowWrap == null) { + if (windowWrap == null || isWindowDeprecated(windowWrap)) { continue; } result.add(windowWrap); @@ -103,7 +114,7 @@ public abstract class LeapArray { for (int i = 0; i < array.length(); i++) { WindowWrap windowWrap = array.get(i); - if (windowWrap == null) { + if (windowWrap == null || isWindowDeprecated(windowWrap)) { continue; } result.add(windowWrap.value()); diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/statistic/base/Window.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/statistic/base/Window.java index da31e8e6..9c0fea76 100755 --- a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/statistic/base/Window.java +++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/statistic/base/Window.java @@ -31,22 +31,26 @@ public class Window { private final LongAdder minRt = new LongAdder(); public Window() { + initMinRt(); + } + + private void initMinRt() { minRt.add(4900); } /** * Clean the adders and reset window to provided start time. * - * @param startTime the start time of the window * @return new clean window */ - Window resetTo(long startTime) { + public Window reset() { pass.reset(); block.reset(); exception.reset(); rt.reset(); success.reset(); minRt.reset(); + initMinRt(); return this; } diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/statistic/base/WindowWrap.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/statistic/base/WindowWrap.java index 7d7a0b51..e8529669 100755 --- a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/statistic/base/WindowWrap.java +++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/statistic/base/WindowWrap.java @@ -65,4 +65,18 @@ public class WindowWrap { public void setValue(T value) { this.value = value; } + + public WindowWrap resetTo(long startTime) { + this.windowStart = startTime; + return this; + } + + @Override + public String toString() { + return "WindowWrap{" + + "windowLength=" + windowLength + + ", windowStart=" + windowStart + + ", value=" + value + + '}'; + } } diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/statistic/metric/WindowLeapArray.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/statistic/metric/WindowLeapArray.java index 70f60a92..82a94554 100755 --- a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/statistic/metric/WindowLeapArray.java +++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/statistic/metric/WindowLeapArray.java @@ -29,18 +29,26 @@ import com.alibaba.csp.sentinel.slots.statistic.base.WindowWrap; */ public class WindowLeapArray extends LeapArray { - private final int timeLength; - public WindowLeapArray(int windowLengthInMs, int intervalInSec) { super(windowLengthInMs, intervalInSec); - timeLength = intervalInSec * 1000; } private ReentrantLock addLock = new ReentrantLock(); + /** + * Reset current window to provided start time and reset all counters. + * + * @param startTime the start time of the window + * @return new clean window wrap + */ + private WindowWrap resetWindowTo(WindowWrap w, long startTime) { + w.resetTo(startTime); + w.value().reset(); + return w; + } + @Override public WindowWrap currentWindow(long time) { - long timeId = time / windowLength; // Calculate current index. int idx = (int)(timeId % array.length()); @@ -62,29 +70,17 @@ public class WindowLeapArray extends LeapArray { } else if (time > old.windowStart()) { if (addLock.tryLock()) { try { - WindowWrap window = new WindowWrap(windowLength, time, new Window()); - if (array.compareAndSet(idx, old, window)) { - for (int i = 0; i < array.length(); i++) { - WindowWrap tmp = array.get(i); - if (tmp == null) { - continue; - } else { - if (tmp.windowStart() < time - timeLength) { - array.set(i, null); - } - } - } - return window; - } + // if (old is deprecated) then [LOCK] resetTo currentTime. + return resetWindowTo(old, time); } finally { addLock.unlock(); } - } else { Thread.yield(); } } else if (time < old.windowStart()) { + // Cannot go through here. return new WindowWrap(windowLength, time, new Window()); } } diff --git a/sentinel-core/src/test/java/com/alibaba/csp/sentinel/base/metric/WindowLeapArrayTest.java b/sentinel-core/src/test/java/com/alibaba/csp/sentinel/base/metric/WindowLeapArrayTest.java index 84390c57..63a60c03 100755 --- a/sentinel-core/src/test/java/com/alibaba/csp/sentinel/base/metric/WindowLeapArrayTest.java +++ b/sentinel-core/src/test/java/com/alibaba/csp/sentinel/base/metric/WindowLeapArrayTest.java @@ -104,7 +104,7 @@ public class WindowLeapArrayTest { assertEquals(0L, currentWindow.block()); } - @Test + @Deprecated public void testWindowDeprecatedRefresh() { WindowLeapArray leapArray = new WindowLeapArray(windowLengthInMs, intervalInSec); final int len = intervalInSec * 1000 / windowLengthInMs; @@ -160,7 +160,34 @@ public class WindowLeapArrayTest { } @Test - public void testListWindows() { + public void testListWindowsResetOld() throws Exception { + final int windowLengthInMs = 100; + final int intervalInSec = 1; + final int intervalInMs = intervalInSec * 1000; + + WindowLeapArray leapArray = new WindowLeapArray(windowLengthInMs, intervalInSec); + long time = TimeUtil.currentTimeMillis(); + + Set> windowWraps = new HashSet>(); + + windowWraps.add(leapArray.currentWindow(time)); + windowWraps.add(leapArray.currentWindow(time + windowLengthInMs)); + + List> list = leapArray.list(); + for (WindowWrap wrap : list) { + assertTrue(windowWraps.contains(wrap)); + } + + Thread.sleep(windowLengthInMs + intervalInMs); + + // This will replace the deprecated bucket, so all deprecated buckets will be reset. + leapArray.currentWindow(time + windowLengthInMs + intervalInMs).value().addPass(); + + assertEquals(1, leapArray.list().size()); + } + + @Test + public void testListWindowsNewBucket() throws Exception { final int windowLengthInMs = 100; final int intervalInSec = 1; @@ -172,12 +199,16 @@ public class WindowLeapArrayTest { windowWraps.add(leapArray.currentWindow(time)); windowWraps.add(leapArray.currentWindow(time + windowLengthInMs)); + Thread.sleep(intervalInSec * 1000 + windowLengthInMs * 3); + List> list = leapArray.list(); for (WindowWrap wrap : list) { assertTrue(windowWraps.contains(wrap)); } - leapArray.currentWindow(time + windowLengthInMs * 20 + intervalInSec * 1000).value().addPass(); + // This won't hit deprecated bucket, so no deprecated buckets will be reset. + // But deprecated buckets can be filtered when collecting list. + leapArray.currentWindow(TimeUtil.currentTimeMillis()).value().addPass(); assertEquals(1, leapArray.list().size()); }