Skip to content
Snippets Groups Projects
Commit 3b8c06d8 authored by Sean Busbey's avatar Sean Busbey
Browse files

Merge pull request #656 from manolama/tsgen

[core] Add a UnixEpochTimestampGenerator
parents afb90c30 8862c119
No related branches found
No related tags found
No related merge requests found
/**
* Copyright (c) 2016 YCSB contributors. All rights reserved.
*
* 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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;
/**
* A generator that produces Unix epoch timestamps in seconds, milli, micro or
* nanoseconds and increments the stamp a given interval each time
* {@link #nextValue()} is called. The result is emitted as a long in the same
* way calls to {@code System.currentTimeMillis()} and
* {@code System.nanoTime()} behave.
* <p>
* By default, the current system time of the host is used as the starting
* timestamp. Calling {@link #initalizeTimestamp(long)} can adjust the timestamp
* back or forward in time. For example, if a workload will generate an hour of
* data at 1 minute intervals, then to set the start timestamp an hour in the past
* from the current run, use:
* <pre>{@code
* UnixEpochTimestampGenerator generator = new UnixEpochTimestampGenerator();
* generator.initalizeTimestamp(-60);
* }</pre>
* A constructor is also present for setting an explicit start time.
* Negative intervals are supported as well for iterating back in time.
* <p>
* WARNING: This generator is not thread safe and should not called from multiple
* threads.
*/
public class UnixEpochTimestampGenerator extends Generator<Long> {
/** The current timestamp that will be incremented. */
private long currentTimestamp;
/** The last used timestamp. Should always be one interval behind current. */
private long lastTimestamp;
/** The interval to increment by. Multiplied by {@link #timeUnits}. */
private long interval;
/** The units of time the interval represents. */
private TimeUnit timeUnits;
/**
* Default ctor with the current system time and a 60 second interval.
*/
public UnixEpochTimestampGenerator() {
this(60, TimeUnit.SECONDS);
}
/**
* Ctor that uses the current system time as current.
* @param interval The interval for incrementing the timestamp.
* @param timeUnits The units of time the increment represents.
*/
public UnixEpochTimestampGenerator(final long interval, final TimeUnit timeUnits) {
this.interval = interval;
this.timeUnits = timeUnits;
// move the first timestamp by 1 interval so that the first call to nextValue
// returns this timestamp
initalizeTimestamp(-1);
currentTimestamp -= getOffset(1);
lastTimestamp = currentTimestamp;
}
/**
* Ctor for supplying a starting timestamp.
* @param interval The interval for incrementing the timestamp.
* @param timeUnits The units of time the increment represents.
* @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()}
* or seconds and any interval above, {@code System.currentTimeMillis() / 1000}
*/
public UnixEpochTimestampGenerator(final long interval, final TimeUnit timeUnits,
final long startTimestamp) {
this.interval = interval;
this.timeUnits = timeUnits;
// move the first timestamp by 1 interval so that the first call to nextValue
// returns this timestamp
this.currentTimestamp = startTimestamp - getOffset(1);
lastTimestamp = currentTimestamp - getOffset(1);
}
/**
* Sets the starting timestamp to the current system time plus the interval offset.
* E.g. to set the time an hour in the past, supply a value of {@code -60}.
* @param intervalOffset The interval to increment or decrement by.
*/
public void initalizeTimestamp(final long intervalOffset) {
switch (timeUnits) {
case NANOSECONDS:
currentTimestamp = System.nanoTime() + getOffset(intervalOffset);
break;
case MICROSECONDS:
currentTimestamp = (System.nanoTime() / 1000) + getOffset(intervalOffset);
break;
case MILLISECONDS:
currentTimestamp = System.currentTimeMillis() + getOffset(intervalOffset);
break;
case SECONDS:
currentTimestamp = (System.currentTimeMillis() / 1000) +
getOffset(intervalOffset);
break;
case MINUTES:
currentTimestamp = (System.currentTimeMillis() / 1000) +
getOffset(intervalOffset);
break;
case HOURS:
currentTimestamp = (System.currentTimeMillis() / 1000) +
getOffset(intervalOffset);
break;
case DAYS:
currentTimestamp = (System.currentTimeMillis() / 1000) +
getOffset(intervalOffset);
break;
default:
throw new IllegalArgumentException("Unhandled time unit type: " + timeUnits);
}
}
@Override
public Long nextValue() {
lastTimestamp = currentTimestamp;
currentTimestamp += getOffset(1);
return currentTimestamp;
}
/**
* Returns the proper increment offset to use given the interval and timeunits.
* @param intervalOffset The amount of offset to multiply by.
* @return An offset value to adjust the timestamp by.
*/
public long getOffset(final long intervalOffset) {
switch (timeUnits) {
case NANOSECONDS:
case MICROSECONDS:
case MILLISECONDS:
case SECONDS:
return intervalOffset * interval;
case MINUTES:
return intervalOffset * interval * (long) 60;
case HOURS:
return intervalOffset * interval * (long) (60 * 60);
case DAYS:
return intervalOffset * interval * (long) (60 * 60 * 24);
default:
throw new IllegalArgumentException("Unhandled time unit type: " + timeUnits);
}
}
@Override
public Long lastValue() {
return lastTimestamp;
}
/** @return The current timestamp as set by the last call to {@link #nextValue()} */
public long currentValue() {
return currentTimestamp;
}
}
/**
* Copyright (c) 2016 YCSB contributors. All rights reserved.
*
* 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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 java.util.concurrent.TimeUnit;
import org.testng.annotations.Test;
public class TestUnixEpochTimestampGenerator {
@Test
public void defaultCtor() throws Exception {
final UnixEpochTimestampGenerator generator =
new UnixEpochTimestampGenerator();
final long startTime = generator.currentValue();
assertEquals((long) generator.nextValue(), startTime + 60);
assertEquals((long) generator.lastValue(), startTime);
assertEquals((long) generator.nextValue(), startTime + 120);
assertEquals((long) generator.lastValue(), startTime + 60);
assertEquals((long) generator.nextValue(), startTime + 180);
}
@Test
public void ctorWithIntervalAndUnits() throws Exception {
final UnixEpochTimestampGenerator generator =
new UnixEpochTimestampGenerator(120, TimeUnit.SECONDS);
final long startTime = generator.currentValue();
assertEquals((long) generator.nextValue(), startTime + 120);
assertEquals((long) generator.lastValue(), startTime);
assertEquals((long) generator.nextValue(), startTime + 240);
assertEquals((long) generator.lastValue(), startTime + 120);
}
@Test
public void ctorWithIntervalAndUnitsAndStart() throws Exception {
final UnixEpochTimestampGenerator generator =
new UnixEpochTimestampGenerator(120, TimeUnit.SECONDS, 1072915200L);
assertEquals((long) generator.nextValue(), 1072915200L);
assertEquals((long) generator.lastValue(), 1072915200L - 120);
assertEquals((long) generator.nextValue(), 1072915200L + 120);
assertEquals((long) generator.lastValue(), 1072915200L);
}
@Test
public void variousIntervalsAndUnits() throws Exception {
// negatives could happen, just start and roll back in time
UnixEpochTimestampGenerator generator =
new UnixEpochTimestampGenerator(-60, TimeUnit.SECONDS);
long startTime = generator.currentValue();
assertEquals((long) generator.nextValue(), startTime - 60);
assertEquals((long) generator.lastValue(), startTime);
assertEquals((long) generator.nextValue(), startTime - 120);
assertEquals((long) generator.lastValue(), startTime - 60);
generator = new UnixEpochTimestampGenerator(100, TimeUnit.NANOSECONDS);
startTime = generator.currentValue();
assertEquals((long) generator.nextValue(), startTime + 100);
assertEquals((long) generator.lastValue(), startTime);
assertEquals((long) generator.nextValue(), startTime + 200);
assertEquals((long) generator.lastValue(), startTime + 100);
generator = new UnixEpochTimestampGenerator(100, TimeUnit.MICROSECONDS);
startTime = generator.currentValue();
assertEquals((long) generator.nextValue(), startTime + 100);
assertEquals((long) generator.lastValue(), startTime);
assertEquals((long) generator.nextValue(), startTime + 200);
assertEquals((long) generator.lastValue(), startTime + 100);
generator = new UnixEpochTimestampGenerator(100, TimeUnit.MILLISECONDS);
startTime = generator.currentValue();
assertEquals((long) generator.nextValue(), startTime + 100);
assertEquals((long) generator.lastValue(), startTime);
assertEquals((long) generator.nextValue(), startTime + 200);
assertEquals((long) generator.lastValue(), startTime + 100);
generator = new UnixEpochTimestampGenerator(100, TimeUnit.SECONDS);
startTime = generator.currentValue();
assertEquals((long) generator.nextValue(), startTime + 100);
assertEquals((long) generator.lastValue(), startTime);
assertEquals((long) generator.nextValue(), startTime + 200);
assertEquals((long) generator.lastValue(), startTime + 100);
generator = new UnixEpochTimestampGenerator(1, TimeUnit.MINUTES);
startTime = generator.currentValue();
assertEquals((long) generator.nextValue(), startTime + (1 * 60));
assertEquals((long) generator.lastValue(), startTime);
assertEquals((long) generator.nextValue(), startTime + (2 * 60));
assertEquals((long) generator.lastValue(), startTime + (1 * 60));
generator = new UnixEpochTimestampGenerator(1, TimeUnit.HOURS);
startTime = generator.currentValue();
assertEquals((long) generator.nextValue(), startTime + (1 * 60 * 60));
assertEquals((long) generator.lastValue(), startTime);
assertEquals((long) generator.nextValue(), startTime + (2 * 60 * 60));
assertEquals((long) generator.lastValue(), startTime + (1 * 60 * 60));
generator = new UnixEpochTimestampGenerator(1, TimeUnit.DAYS);
startTime = generator.currentValue();
assertEquals((long) generator.nextValue(), startTime + (1 * 60 * 60 * 24));
assertEquals((long) generator.lastValue(), startTime);
assertEquals((long) generator.nextValue(), startTime + (2 * 60 * 60 * 24));
assertEquals((long) generator.lastValue(), startTime + (1 * 60 * 60 * 24));
}
// TODO - With PowerMockito we could UT the initializeTimestamp(long) call.
// Otherwise it would involve creating more functions and that would get ugly.
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment