-
Notifications
You must be signed in to change notification settings - Fork 2.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[core] Add the RandomDiscreteTimestampGenerator to generate a range of
timestamps in a non-repeating random order. Modify UnixEpochTimestampGenerator so that the random generator can extend it.
- Loading branch information
Showing
3 changed files
with
211 additions
and
6 deletions.
There are no files selected for viewing
119 changes: 119 additions & 0 deletions
119
core/src/main/java/com/yahoo/ycsb/generator/RandomDiscreteTimestampGenerator.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,119 @@ | ||
/** | ||
* Copyright (c) 2017 YCSB contributors. All rights reserved. | ||
* <p> | ||
* Licensed under the Apache License, Version 2.0 (the "License"); you | ||
* may not use this file except in compliance with the License. You | ||
* may obtain a copy of the License at | ||
* <p> | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* <p> | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or | ||
* implied. See the License for the specific language governing | ||
* permissions and limitations under the License. See accompanying | ||
* LICENSE file. | ||
*/ | ||
package com.yahoo.ycsb.generator; | ||
|
||
import java.util.concurrent.TimeUnit; | ||
|
||
import com.yahoo.ycsb.Utils; | ||
|
||
/** | ||
* A generator that picks from a discrete set of offsets from a base Unix Epoch | ||
* timestamp that returns timestamps in a random order with the guarantee that | ||
* each timestamp is only returned once. | ||
* <p> | ||
* TODO - It would be best to implement some kind of psuedo non-repeating random | ||
* generator for this as it's likely OK that some small percentage of values are | ||
* repeated. For now we just generate all of the offsets in an array, shuffle | ||
* it and then iterate over the array. | ||
* <p> | ||
* Note that {@link #MAX_INTERVALS} defines a hard limit on the size of the | ||
* offset array so that we don't completely blow out the heap. | ||
* <p> | ||
* The constructor parameter {@code intervals} determines how many values will be | ||
* returned by the generator. For example, if the {@code interval} is 60 and the | ||
* {@code timeUnits} are set to {@link TimeUnit#SECONDS} and {@code intervals} | ||
* is set to 60, then the consumer can call {@link #nextValue()} 60 times for | ||
* timestamps within an hour. | ||
*/ | ||
public class RandomDiscreteTimestampGenerator extends UnixEpochTimestampGenerator { | ||
|
||
/** A hard limit on the size of the offsets array to a void using too much heap. */ | ||
public static final int MAX_INTERVALS = 16777216; | ||
|
||
/** The total number of intervals for this generator. */ | ||
private final int intervals; | ||
|
||
// can't be primitives due to the generic params on the sort function :( | ||
/** The array of generated offsets from the base time. */ | ||
private final Integer[] offsets; | ||
|
||
/** The current index into the offsets array. */ | ||
private int offsetIndex; | ||
|
||
/** | ||
* Ctor that uses the current system time as current. | ||
* @param interval The interval between timestamps. | ||
* @param timeUnits The time units of the returned Unix Epoch timestamp (as well | ||
* as the units for the interval). | ||
* @param intervals The total number of intervals for the generator. | ||
* @throws IllegalArgumentException if the intervals is larger than {@link #MAX_INTERVALS} | ||
*/ | ||
public RandomDiscreteTimestampGenerator(final long interval, final TimeUnit timeUnits, | ||
final int intervals) { | ||
super(interval, timeUnits); | ||
this.intervals = intervals; | ||
offsets = new Integer[intervals]; | ||
setup(); | ||
} | ||
|
||
/** | ||
* Ctor for supplying a starting timestamp. | ||
* The interval between timestamps. | ||
* @param timeUnits The time units of the returned Unix Epoch timestamp (as well | ||
* as the units for the interval). | ||
* @param startTimestamp The start timestamp to use. | ||
* NOTE that this must match the time units used for the interval. | ||
* If the units are in nanoseconds, provide a nanosecond timestamp {@code System.nanoTime()} | ||
* or in microseconds, {@code System.nanoTime() / 1000} | ||
* or in millis, {@code System.currentTimeMillis()} | ||
* @param intervals The total number of intervals for the generator. | ||
* @throws IllegalArgumentException if the intervals is larger than {@link #MAX_INTERVALS} | ||
*/ | ||
public RandomDiscreteTimestampGenerator(final long interval, final TimeUnit timeUnits, | ||
final long startTimestamp, final int intervals) { | ||
super(interval, timeUnits, startTimestamp); | ||
this.intervals = intervals; | ||
offsets = new Integer[intervals]; | ||
setup(); | ||
} | ||
|
||
/** | ||
* Generates the offsets and shuffles the array. | ||
*/ | ||
private void setup() { | ||
if (intervals > MAX_INTERVALS) { | ||
throw new IllegalArgumentException("Too many intervals for the in-memory " | ||
+ "array. The limit is " + MAX_INTERVALS + "."); | ||
} | ||
offsetIndex = 0; | ||
for (int i = 0; i < intervals; i++) { | ||
offsets[i] = i; | ||
} | ||
Utils.shuffleArray(offsets); | ||
} | ||
|
||
@Override | ||
public Long nextValue() { | ||
if (offsetIndex >= offsets.length) { | ||
throw new IllegalStateException("Reached the end of the random timestamp " | ||
+ "intervals: " + offsetIndex); | ||
} | ||
lastTimestamp = currentTimestamp; | ||
currentTimestamp = startTimestamp + (offsets[offsetIndex++] * getOffset(1)); | ||
return currentTimestamp; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
75 changes: 75 additions & 0 deletions
75
core/src/test/java/com/yahoo/ycsb/generator/TestRandomDiscreteTimestampGenerator.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
/** | ||
* Copyright (c) 2017 YCSB contributors. All rights reserved. | ||
* <p> | ||
* Licensed under the Apache License, Version 2.0 (the "License"); you | ||
* may not use this file except in compliance with the License. You | ||
* may obtain a copy of the License at | ||
* <p> | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* <p> | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or | ||
* implied. See the License for the specific language governing | ||
* permissions and limitations under the License. See accompanying | ||
* LICENSE file. | ||
*/ | ||
package com.yahoo.ycsb.generator; | ||
|
||
import static org.testng.Assert.assertEquals; | ||
import static org.testng.Assert.fail; | ||
|
||
import java.util.Collections; | ||
import java.util.List; | ||
import java.util.concurrent.TimeUnit; | ||
|
||
import org.testng.annotations.Test; | ||
import org.testng.collections.Lists; | ||
|
||
public class TestRandomDiscreteTimestampGenerator { | ||
|
||
@Test | ||
public void systemTime() throws Exception { | ||
final RandomDiscreteTimestampGenerator generator = | ||
new RandomDiscreteTimestampGenerator(60, TimeUnit.SECONDS, 60); | ||
List<Long> generated = Lists.newArrayList(); | ||
for (int i = 0; i < 60; i++) { | ||
generated.add(generator.nextValue()); | ||
} | ||
assertEquals(generated.size(), 60); | ||
try { | ||
generator.nextValue(); | ||
fail("Expected IllegalStateException"); | ||
} catch (IllegalStateException e) { } | ||
} | ||
|
||
@Test | ||
public void withStartTime() throws Exception { | ||
final RandomDiscreteTimestampGenerator generator = | ||
new RandomDiscreteTimestampGenerator(60, TimeUnit.SECONDS, 1072915200L, 60); | ||
List<Long> generated = Lists.newArrayList(); | ||
for (int i = 0; i < 60; i++) { | ||
generated.add(generator.nextValue()); | ||
} | ||
assertEquals(generated.size(), 60); | ||
Collections.sort(generated); | ||
long ts = 1072915200L - 60; // starts 1 interval in the past | ||
for (final long t : generated) { | ||
assertEquals(t, ts); | ||
ts += 60; | ||
} | ||
try { | ||
generator.nextValue(); | ||
fail("Expected IllegalStateException"); | ||
} catch (IllegalStateException e) { } | ||
} | ||
|
||
@Test (expectedExceptions = IllegalArgumentException.class) | ||
public void tooLarge() throws Exception { | ||
new RandomDiscreteTimestampGenerator(60, TimeUnit.SECONDS, | ||
RandomDiscreteTimestampGenerator.MAX_INTERVALS + 1); | ||
} | ||
|
||
//TODO - With PowerMockito we could UT the initializeTimestamp(long) call. | ||
// Otherwise it would involve creating more functions and that would get ugly. | ||
} |