Skip to content

Commit

Permalink
prepare release documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
vladimir-bukhtoyarov committed Nov 10, 2016
1 parent f7945dc commit 0f6f848
Show file tree
Hide file tree
Showing 7 changed files with 104 additions and 28 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,12 @@ public class TopBenchmark {
public static class TopState {

final Top chunkedTop_1 = Top.builder(1)
.resetAllPositionsPeriodicallyByChunks(Duration.ofSeconds(4), 4)
.resetPositionsPeriodicallyByChunks(Duration.ofSeconds(4), 4)
.withSnapshotCachingDuration(Duration.ZERO)
.build();

final Top chunkedTop_10 = Top.builder(10)
.resetAllPositionsPeriodicallyByChunks(Duration.ofSeconds(4), 4)
.resetPositionsPeriodicallyByChunks(Duration.ofSeconds(4), 4)
.withSnapshotCachingDuration(Duration.ZERO)
.build();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
import java.util.function.Supplier;

/**
* The entry point of metrics-core-hdr library which can be used for creation and registration histograms, timers and reservoirs.
* The builder for creation and registration histograms, timers and reservoirs.
*
* <p><br> Basic examples of usage:
* <pre><code>
Expand Down Expand Up @@ -82,6 +82,7 @@ public HdrBuilder() {
*
* @return this builder instance
* @see #resetReservoirPeriodically(Duration)
* @see #resetReservoirPeriodicallyByChunks(Duration, int)
* @see #neverResetReservoir()
*/
public HdrBuilder resetReservoirOnSnapshot() {
Expand Down
85 changes: 72 additions & 13 deletions src/main/java/com/github/metricscore/hdr/top/TopBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@

import java.time.Duration;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadFactory;

/**
* The builder for {@link Top}.
Expand Down Expand Up @@ -154,9 +155,14 @@ public TopBuilder withSnapshotCachingDuration(Duration snapshotCachingDuration)
}

/**
* Specifies the max length of description position int the top. The characters upper {@code maxLengthOfQueryDescription} limit will be truncated
*
* @param maxLengthOfQueryDescription
* @return
* <p>
* The default value is 1000 symbol {@link #DEFAULT_MAX_LENGTH_OF_QUERY_DESCRIPTION}.
*
* @param maxLengthOfQueryDescription the max length of description position int the top.
*
* @return this builder instance
*/
public TopBuilder withMaxLengthOfQueryDescription(int maxLengthOfQueryDescription) {
if (maxLengthOfQueryDescription < MIN_LENGTH_OF_QUERY_DESCRIPTION) {
Expand All @@ -170,8 +176,11 @@ public TopBuilder withMaxLengthOfQueryDescription(int maxLengthOfQueryDescriptio
}

/**
* Replaces default clock.
* Most likely you should never use this method, because replacing time measuring has sense only for unit testing.
*
* @param clock the abstraction over time
*
* @param clock
* @return this builder instance
*/
public TopBuilder withClock(Clock clock) {
Expand All @@ -183,8 +192,17 @@ public TopBuilder withClock(Clock clock) {
}

/**
* Configures the executor which will be used if any of {@link #resetAllPositionsPeriodically(Duration)} (Duration)} or {@link #resetPositionsPeriodicallyByChunks(Duration, int)} (Duration, int)} (Duration)}.
*
* <p>
* Normally you should not use this method because of default executor provided by {@link ResilientExecutionUtil#getBackgroundExecutor()} is quietly enough for mostly use cases.
* </p>
*
* <p>
* You can use this method for example inside JEE environments with enabled SecurityManager,
* in case of {@link ResilientExecutionUtil#setThreadFactory(ThreadFactory)} is not enough to meat security rules.
* </p>
*
* @param backgroundExecutor
* @return this builder instance
*/
public TopBuilder withBackgroundExecutor(Executor backgroundExecutor) {
Expand All @@ -196,26 +214,52 @@ public TopBuilder withBackgroundExecutor(Executor backgroundExecutor) {
}

/**
* Top configured with this strategy will store all values since the top was created.
*
* <p>This is default strategy for {@link TopBuilder}.
* This strategy is useless for long running applications, because very slow queries happen in the past
* will not provide chances to fresh queries to take place in the top.
* So, it is strongly recommended to switch eviction strategy to one of:
* <ul>
* <li>{@link #resetAllPositionsPeriodically(Duration)}</li>
* <li>{@link #resetPositionsPeriodicallyByChunks(Duration, int)}</li>
* <li>{@link #resetAllPositionsOnSnapshot()}</li>
* </ul>
*
* @return this builder instance
*
* @see #resetPositionsPeriodicallyByChunks(Duration, int)
* @see #resetAllPositionsPeriodically(Duration)
* @see #resetAllPositionsOnSnapshot()
*/
public TopBuilder neverResetPositions() {
this.factory = TopFactory.UNIFORM;
return this;
}

/**
* Top configured with this strategy will be cleared each time when {@link Top#getPositionsInDescendingOrder()} invoked.
*
* @return this builder instance
* @see #resetPositionsPeriodicallyByChunks(Duration, int)
* @see #resetAllPositionsPeriodically(Duration)
*/
public TopBuilder resetAllPositionsOnSnapshot() {
this.factory = TopFactory.RESET_ON_SNAPSHOT;
return this;
}

/**
* Top configured with this strategy will be cleared at all after each {@code intervalBetweenResetting} elapsed.
*
* @param intervalBetweenResetting
* <p>
* If You use this strategy inside JEE environment,
* then it would be better to call {@code ResilientExecutionUtil.getInstance().shutdownBackgroundExecutor()}
* once in application shutdown listener,
* in order to avoid leaking reference to classloader through the thread which this library creates for resetting of top in background.
* </p>
*
* @param intervalBetweenResetting specifies how often need to reset the top
* @return this builder instance
*/
public TopBuilder resetAllPositionsPeriodically(Duration intervalBetweenResetting) {
Expand All @@ -235,26 +279,41 @@ public TopBuilder resetAllPositionsPeriodically(Duration intervalBetweenResettin
}

/**
* Top configured with this strategy will be divided to <tt>numberChunks</tt> parts,
* and one chunk will be cleared after each <tt>rollingTimeWindow / numberChunks</tt> elapsed.
* This strategy is more smoothly then <tt>resetAllPositionsPeriodically</tt> because top never zeroed at whole,
* so user experience provided by <tt>resetPositionsPeriodicallyByChunks</tt> should look more pretty.
* <p>
* The value recorded to top will take affect at least <tt>rollingTimeWindow</tt> and at most <tt>rollingTimeWindow *(1 + 1/numberChunks)</tt> time,
* for example when you configure <tt>rollingTimeWindow=60 seconds and numberChunks=6</tt> then each value recorded to top will be stored at <tt>60-70 seconds</tt>
* </p>
*
* <p>
* If You use this strategy inside JEE environment,
* then it would be better to call {@code ResilientExecutionUtil.getInstance().shutdownBackgroundExecutor()}
* once in application shutdown listener,
* in order to avoid leaking reference to classloader through the thread which this library creates for rotation of top in background.
* </p>
*
* @param rollingWindow
* @param numberChunks
* @param rollingTimeWindow the total rolling time window, any value recorded to top will not be evicted from it at least <tt>rollingTimeWindow</tt>
* @param numberChunks specifies number of chunks by which the top will be slitted
* @return this builder instance
*/
public TopBuilder resetAllPositionsPeriodicallyByChunks(Duration rollingWindow, int numberChunks) {
public TopBuilder resetPositionsPeriodicallyByChunks(Duration rollingTimeWindow, int numberChunks) {
if (numberChunks > MAX_CHUNKS) {
throw new IllegalArgumentException("numberChunks should be <= " + MAX_CHUNKS);
}
if (numberChunks < 2) {
throw new IllegalArgumentException("numberChunks should be >= 1");
}
if (rollingWindow == null) {
throw new IllegalArgumentException("rollingWindow should not be null");
if (rollingTimeWindow == null) {
throw new IllegalArgumentException("rollingTimeWindow should not be null");
}
if (rollingWindow.isNegative()) {
throw new IllegalArgumentException("rollingWindow should not be negative");
if (rollingTimeWindow.isNegative()) {
throw new IllegalArgumentException("rollingTimeWindow should not be negative");
}

long intervalBetweenResettingMillis = rollingWindow.toMillis() / numberChunks;
long intervalBetweenResettingMillis = rollingTimeWindow.toMillis() / numberChunks;
if (intervalBetweenResettingMillis < MIN_CHUNK_RESETTING_INTERVAL_MILLIS) {
String msg = "interval between resetting one chunk should be >= " + MIN_CHUNK_RESETTING_INTERVAL_MILLIS + " millis";
throw new IllegalArgumentException(msg);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
* </ul>
* The "latency" gauges have {@link BigDecimal} type, the "latencyUnit" and "description" gauges have {@link String} type.
* The number in the gauge name represents position in the top in descending order, the "0" is the slowest query.
* </p>
*
*/
public class TopMetricSet implements MetricSet {

Expand Down
10 changes: 5 additions & 5 deletions src/test/java/com/github/metricscore/hdr/top/TopBuilderTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -99,27 +99,27 @@ public void tooShortIntervalBetweenResettingShouldBeDisallowed() {

@Test(expected = IllegalArgumentException.class)
public void tooManyChunksShouldBeDisallowed() {
Top.builder(1).resetAllPositionsPeriodicallyByChunks(Duration.ofDays(1), TopBuilder.MAX_CHUNKS + 1);
Top.builder(1).resetPositionsPeriodicallyByChunks(Duration.ofDays(1), TopBuilder.MAX_CHUNKS + 1);
}

@Test(expected = IllegalArgumentException.class)
public void lesserThanTwoChunksShouldBeDisallowed() {
Top.builder(1).resetAllPositionsPeriodicallyByChunks(Duration.ofDays(1), 1);
Top.builder(1).resetPositionsPeriodicallyByChunks(Duration.ofDays(1), 1);
}

@Test(expected = IllegalArgumentException.class)
public void nullRollingWindowShouldBeDisallowed() {
Top.builder(1).resetAllPositionsPeriodicallyByChunks(null, TopBuilder.MAX_CHUNKS);
Top.builder(1).resetPositionsPeriodicallyByChunks(null, TopBuilder.MAX_CHUNKS);
}

@Test(expected = IllegalArgumentException.class)
public void tooShortChunkTtlShouldBeDisallowed() {
Top.builder(1).resetAllPositionsPeriodicallyByChunks(Duration.ofMillis(TopBuilder.MIN_CHUNK_RESETTING_INTERVAL_MILLIS * 10 - 1), 10);
Top.builder(1).resetPositionsPeriodicallyByChunks(Duration.ofMillis(TopBuilder.MIN_CHUNK_RESETTING_INTERVAL_MILLIS * 10 - 1), 10);
}

@Test(expected = IllegalArgumentException.class)
public void negativeChunkTtlShouldBeDisallowed() {
Top.builder(1).resetAllPositionsPeriodicallyByChunks(Duration.ofMillis(-2000), 2);
Top.builder(1).resetPositionsPeriodicallyByChunks(Duration.ofMillis(-2000), 2);
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public class ResetByChunksTopTest {
public void testCommonAspects() {
for (int i = 1; i <= 2; i++) {
Top top = Top.builder(i)
.resetAllPositionsPeriodicallyByChunks(Duration.ofDays(1), 3)
.resetPositionsPeriodicallyByChunks(Duration.ofDays(1), 3)
.withSnapshotCachingDuration(Duration.ZERO)
.withLatencyThreshold(Duration.ofMillis(100))
.withMaxLengthOfQueryDescription(1000)
Expand All @@ -49,7 +49,7 @@ public void test_size_1() throws Exception {
AtomicLong currentTimeMillis = new AtomicLong(0L);
Clock clock = Clock.mock(currentTimeMillis);
Top top = Top.builder(1)
.resetAllPositionsPeriodicallyByChunks(Duration.ofSeconds(3), 3)
.resetPositionsPeriodicallyByChunks(Duration.ofSeconds(3), 3)
.withSnapshotCachingDuration(Duration.ZERO)
.withClock(clock)
.withBackgroundExecutor(MockExecutor.INSTANCE)
Expand Down Expand Up @@ -123,7 +123,7 @@ public void test_size_3() throws Exception {
AtomicLong currentTimeMillis = new AtomicLong(0L);
Clock clock = Clock.mock(currentTimeMillis);
Top top = Top.builder(3)
.resetAllPositionsPeriodicallyByChunks(Duration.ofSeconds(3), 3)
.resetPositionsPeriodicallyByChunks(Duration.ofSeconds(3), 3)
.withSnapshotCachingDuration(Duration.ZERO)
.withClock(clock)
.withBackgroundExecutor(MockExecutor.INSTANCE)
Expand Down Expand Up @@ -196,15 +196,15 @@ public void test_size_3() throws Exception {
public void testToString() {
for (int i = 1; i <= 2; i++) {
System.out.println(Top.builder(i)
.resetAllPositionsPeriodicallyByChunks(Duration.ofDays(1), 3)
.resetPositionsPeriodicallyByChunks(Duration.ofDays(1), 3)
.build());
}
}

@Test(timeout = 32000)
public void testThatConcurrentThreadsNotHung_1() throws InterruptedException {
Top top = Top.builder(1)
.resetAllPositionsPeriodicallyByChunks(Duration.ofSeconds(2), 2)
.resetPositionsPeriodicallyByChunks(Duration.ofSeconds(2), 2)
.withSnapshotCachingDuration(Duration.ZERO)
.build();
TopTestUtil.runInParallel(top, Duration.ofSeconds(30), 0, 10_000);
Expand All @@ -213,7 +213,7 @@ public void testThatConcurrentThreadsNotHung_1() throws InterruptedException {
@Test(timeout = 32000)
public void testThatConcurrentThreadsNotHung_3() throws InterruptedException {
Top top = Top.builder(3)
.resetAllPositionsPeriodicallyByChunks(Duration.ofSeconds(2), 2)
.resetPositionsPeriodicallyByChunks(Duration.ofSeconds(2), 2)
.withSnapshotCachingDuration(Duration.ZERO)
.build();
TopTestUtil.runInParallel(top, Duration.ofSeconds(30), 0, 10_000);
Expand Down
16 changes: 16 additions & 0 deletions top.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Top

*TODO:* complete documentation

## How to add Top to MetricRegistry?
The all of implementations of Top mentioned above do not implement of any MetricCore interface,
this decision was taken in order to provide ability to use Top without dependency from metrics-core library.
So you need to register Top manually as MetricSet in **MetricRegistry**, for example:
```java
Top top = Top.builder(3).resetAllPositionsOnSnapshot().build();

TimeUnit latencyOutputUnit = TimeUnit.MILLISECONDS;
int digitsAfterDecimalPoint = 5;
MetricSet metricSet = new TopMetricSet("my-top", top, latencyOutputUnit, digitsAfterDecimalPoint);
registry.registerAll(metricSet);
```

0 comments on commit 0f6f848

Please sign in to comment.