Change clock and converter to use epoch nanos (#636)

Signed-off-by: Bogdan Drutu <bogdandrutu@gmail.com>
This commit is contained in:
Bogdan Drutu 2019-10-25 13:35:11 -07:00 committed by GitHub
parent ab00d64b4c
commit bd14f39727
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 110 additions and 149 deletions

View File

@ -35,7 +35,8 @@ public abstract class Timestamp {
private static final long MAX_SECONDS = 315576000000L;
private static final int MAX_NANOS = 999999999;
private static final long MILLIS_PER_SECOND = 1000L;
private static final long NANOS_PER_MILLI = 1000 * 1000;
private static final long NANOS_PER_SECOND = 1000L * 1000L * 1000L;
private static final long NANOS_PER_MILLI = 1000L * 1000L;
Timestamp() {}
@ -85,6 +86,21 @@ public abstract class Timestamp {
return create(secs, (int) (mos * NANOS_PER_MILLI)); // Safe int * NANOS_PER_MILLI
}
/**
* Creates a new timestamp from the given milliseconds.
*
* @param epochNanos the timestamp represented in nanoseconds since epoch.
* @return new {@code Timestamp} with specified fields.
* @throws IllegalArgumentException if the number of milliseconds is out of the range that can be
* represented by {@code Timestamp}.
* @since 0.1.0
*/
public static Timestamp fromNanos(long epochNanos) {
long secs = floorDiv(epochNanos, NANOS_PER_SECOND);
int nanos = (int) floorMod(epochNanos, NANOS_PER_SECOND);
return create(secs, nanos); // Safe int * NANOS_PER_MILLI
}
/**
* Returns the number of seconds since the Unix Epoch represented by this timestamp.
*

View File

@ -16,8 +16,6 @@
package io.opentelemetry.sdk.internal;
import io.opentelemetry.sdk.common.Timestamp;
/**
* Interface for getting the current time.
*
@ -25,12 +23,12 @@ import io.opentelemetry.sdk.common.Timestamp;
*/
public interface Clock {
/**
* Obtains the current instant from this clock.
* Obtains the current epoch timestamp in nanos from this clock.
*
* @return the current instant.
* @return the current epoch timestamp in nanos.
* @since 0.1.0
*/
Timestamp now();
long now();
/**
* Returns a time measurement with nanosecond precision that can only be used to calculate elapsed
@ -40,5 +38,5 @@ public interface Clock {
* time.
* @since 0.1.0
*/
long nowNanos();
long nanoTime();
}

View File

@ -16,7 +16,7 @@
package io.opentelemetry.sdk.internal;
import io.opentelemetry.sdk.common.Timestamp;
import java.util.concurrent.TimeUnit;
import javax.annotation.concurrent.ThreadSafe;
/** A {@link Clock} that uses {@link System#currentTimeMillis()} and {@link System#nanoTime()}. */
@ -37,12 +37,12 @@ public final class MillisClock implements Clock {
}
@Override
public Timestamp now() {
return Timestamp.fromMillis(System.currentTimeMillis());
public long now() {
return TimeUnit.MILLISECONDS.toNanos(System.currentTimeMillis());
}
@Override
public long nowNanos() {
public long nanoTime() {
return System.nanoTime();
}
}

View File

@ -17,9 +17,8 @@
package io.opentelemetry.sdk.internal;
import static io.opentelemetry.sdk.internal.TimestampConverter.NANOS_PER_MILLI;
import static io.opentelemetry.sdk.internal.TimestampConverter.NANOS_PER_SECOND;
import io.opentelemetry.sdk.common.Timestamp;
import java.util.concurrent.TimeUnit;
import javax.annotation.concurrent.GuardedBy;
import javax.annotation.concurrent.ThreadSafe;
@ -32,10 +31,10 @@ import javax.annotation.concurrent.ThreadSafe;
public class TestClock implements Clock {
@GuardedBy("this")
private Timestamp currentTimestamp;
private long currentEpochNanos;
private TestClock(Timestamp timestamp) {
currentTimestamp = timestamp;
private TestClock(long epochNanos) {
currentEpochNanos = epochNanos;
}
/**
@ -46,28 +45,28 @@ public class TestClock implements Clock {
*/
public static TestClock create() {
// Set Time to Tuesday, May 7, 2019 12:00:00 AM GMT-07:00 DST
return create(Timestamp.fromMillis(1_557_212_400_000L));
return create(TimeUnit.MILLISECONDS.toNanos(1_557_212_400_000L));
}
/**
* Creates a clock with the given time.
*
* @param timestamp the initial time.
* @param epochNanos the initial time in nanos since epoch.
* @return a new {@code TestClock} with the given time.
* @since 0.1.0
*/
public static TestClock create(Timestamp timestamp) {
return new TestClock(timestamp);
public static TestClock create(long epochNanos) {
return new TestClock(epochNanos);
}
/**
* Sets the time.
*
* @param timestamp the new time.
* @param epochNanos the new time.
* @since 0.1.0
*/
public synchronized void setTime(Timestamp timestamp) {
currentTimestamp = timestamp;
public synchronized void setTime(long epochNanos) {
currentEpochNanos = epochNanos;
}
/**
@ -77,27 +76,17 @@ public class TestClock implements Clock {
* @since 0.1.0
*/
public synchronized void advanceMillis(long millis) {
long incomingSeconds = millis / 1000;
long remainingMillis = millis % 1000;
long remainingNanos = remainingMillis * NANOS_PER_MILLI;
long newSeconds = incomingSeconds + currentTimestamp.getSeconds();
long newNanos = remainingNanos + currentTimestamp.getNanos();
if (newNanos >= NANOS_PER_SECOND) {
newSeconds += newNanos / NANOS_PER_SECOND;
newNanos = newNanos % NANOS_PER_SECOND;
}
currentTimestamp = Timestamp.create(newSeconds, (int) newNanos);
long nanos = millis * NANOS_PER_MILLI;
currentEpochNanos += nanos;
}
@Override
public synchronized Timestamp now() {
return currentTimestamp;
public synchronized long now() {
return currentEpochNanos;
}
@Override
public synchronized long nowNanos() {
return (currentTimestamp.getSeconds() * NANOS_PER_SECOND) + currentTimestamp.getNanos();
public synchronized long nanoTime() {
return currentEpochNanos;
}
}

View File

@ -29,7 +29,7 @@ public class TimestampConverter {
static final long NANOS_PER_SECOND = 1_000_000_000;
static final long NANOS_PER_MILLI = 1_000_000;
private final Timestamp timestamp;
private final long epochNanos;
private final long nanoTime;
/**
@ -39,7 +39,7 @@ public class TimestampConverter {
* @return a {@code TimestampConverter} initialized to now.
*/
public static TimestampConverter now(Clock clock) {
return new TimestampConverter(clock.now(), clock.nowNanos());
return new TimestampConverter(clock.now(), clock.nanoTime());
}
/**
@ -48,21 +48,13 @@ public class TimestampConverter {
* @param nanoTime value to convert.
* @return the {@code Timestamp} representation of the {@code time}.
*/
public Timestamp convertNanoTime(long nanoTime) {
public long convertNanoTime(long nanoTime) {
long deltaNanos = nanoTime - this.nanoTime;
long seconds = timestamp.getSeconds() + (deltaNanos / NANOS_PER_SECOND);
long nanos = timestamp.getNanos() + (deltaNanos % NANOS_PER_SECOND);
if (nanos >= NANOS_PER_SECOND) {
seconds += nanos / NANOS_PER_SECOND;
nanos = nanos % NANOS_PER_SECOND;
}
return Timestamp.create(seconds, (int) nanos);
return epochNanos + deltaNanos;
}
private TimestampConverter(Timestamp timestamp, long nanoTime) {
this.timestamp = timestamp;
private TimestampConverter(long epochNanos, long nanoTime) {
this.epochNanos = epochNanos;
this.nanoTime = nanoTime;
}
}

View File

@ -156,8 +156,10 @@ final class RecordEventsReadableSpan implements ReadableSpan, Span {
@Override
public SpanData toSpanData() {
Timestamp startTimestamp = timestampConverter.convertNanoTime(startNanoTime);
Timestamp endTimestamp = timestampConverter.convertNanoTime(getEndNanoTime());
Timestamp startTimestamp =
Timestamp.fromNanos(timestampConverter.convertNanoTime(startNanoTime));
Timestamp endTimestamp =
Timestamp.fromNanos(timestampConverter.convertNanoTime(getEndNanoTime()));
SpanContext spanContext = getSpanContext();
return SpanData.newBuilder()
.setName(getName())
@ -188,7 +190,8 @@ final class RecordEventsReadableSpan implements ReadableSpan, Span {
private static SpanData.TimedEvent adaptTimedEvent(
io.opentelemetry.sdk.trace.TimedEvent sourceEvent, TimestampConverter timestampConverter) {
Timestamp timestamp = timestampConverter.convertNanoTime(sourceEvent.getNanotime());
Timestamp timestamp =
Timestamp.fromNanos(timestampConverter.convertNanoTime(sourceEvent.getNanotime()));
return SpanData.TimedEvent.create(
timestamp, sourceEvent.getName(), sourceEvent.getAttributes());
}
@ -212,7 +215,7 @@ final class RecordEventsReadableSpan implements ReadableSpan, Span {
/**
* Returns the end nano time (see {@link System#nanoTime()}). If the current {@code Span} is not
* ended then returns {@link Clock#nowNanos()}.
* ended then returns {@link Clock#nanoTime()}.
*
* @return the end nano time.
*/
@ -297,7 +300,7 @@ final class RecordEventsReadableSpan implements ReadableSpan, Span {
// Use getEndNanoTimeInternal to avoid over-locking.
@GuardedBy("this")
private long getEndNanoTimeInternal() {
return hasBeenEnded ? endNanoTime : clock.nowNanos();
return hasBeenEnded ? endNanoTime : clock.nanoTime();
}
/**
@ -365,35 +368,35 @@ final class RecordEventsReadableSpan implements ReadableSpan, Span {
@Override
public void addEvent(String name) {
addTimedEvent(TimedEvent.create(clock.nowNanos(), name));
addTimedEvent(TimedEvent.create(clock.nanoTime(), name));
}
// TODO: Use timestamp.
@Override
public void addEvent(String name, long timestamp) {
addTimedEvent(TimedEvent.create(clock.nowNanos(), name));
addTimedEvent(TimedEvent.create(clock.nanoTime(), name));
}
@Override
public void addEvent(String name, Map<String, AttributeValue> attributes) {
addTimedEvent(TimedEvent.create(clock.nowNanos(), name, attributes));
addTimedEvent(TimedEvent.create(clock.nanoTime(), name, attributes));
}
// TODO: Use timestamp.
@Override
public void addEvent(String name, Map<String, AttributeValue> attributes, long timestamp) {
addTimedEvent(TimedEvent.create(clock.nowNanos(), name, attributes));
addTimedEvent(TimedEvent.create(clock.nanoTime(), name, attributes));
}
@Override
public void addEvent(Event event) {
addTimedEvent(TimedEvent.create(clock.nowNanos(), event));
addTimedEvent(TimedEvent.create(clock.nanoTime(), event));
}
// TODO: Use timestamp.
@Override
public void addEvent(Event event, long timestamp) {
addTimedEvent(TimedEvent.create(clock.nowNanos(), event));
addTimedEvent(TimedEvent.create(clock.nanoTime(), event));
}
private void addTimedEvent(TimedEvent timedEvent) {
@ -438,7 +441,7 @@ final class RecordEventsReadableSpan implements ReadableSpan, Span {
logger.log(Level.FINE, "Calling end() on an ended Span.");
return;
}
endNanoTime = clock.nowNanos();
endNanoTime = clock.nanoTime();
hasBeenEnded = true;
}
spanProcessor.onEnd(this);
@ -555,7 +558,7 @@ final class RecordEventsReadableSpan implements ReadableSpan, Span {
this.numberOfChildren = 0;
this.timestampConverter =
timestampConverter != null ? timestampConverter : TimestampConverter.now(clock);
startNanoTime = clock.nowNanos();
startNanoTime = clock.nanoTime();
if (!attributes.isEmpty()) {
getInitializedAttributes().putAll(attributes);
}

View File

@ -1,30 +0,0 @@
/*
* Copyright 2019, OpenTelemetry Authors
*
* 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.
*/
package io.opentelemetry.sdk.internal;
import io.opentelemetry.sdk.common.Timestamp;
final class ClockTestUtil {
static final int NANOS_PER_SECOND = 1000 * 1000 * 1000;
static final int NANOS_PER_MILLI = 1000 * 1000;
static Timestamp createTimestamp(long seconds, int nanos) {
return Timestamp.create(seconds, nanos);
}
private ClockTestUtil() {}
}

View File

@ -28,27 +28,25 @@ public final class TestClockTest {
@Test
public void setAndGetTime() {
TestClock clock = TestClock.create(ClockTestUtil.createTimestamp(1, 2));
assertThat(clock.now()).isEqualTo(ClockTestUtil.createTimestamp(1, 2));
clock.setTime(ClockTestUtil.createTimestamp(3, 4));
assertThat(clock.now()).isEqualTo(ClockTestUtil.createTimestamp(3, 4));
TestClock clock = TestClock.create(1234);
assertThat(clock.now()).isEqualTo(1234);
clock.setTime(9876543210L);
assertThat(clock.now()).isEqualTo(9876543210L);
}
@Test
public void advanceMillis() {
TestClock clock =
TestClock.create(ClockTestUtil.createTimestamp(1, 500 * ClockTestUtil.NANOS_PER_MILLI));
TestClock clock = TestClock.create(1_500_000_000L);
clock.advanceMillis(2600);
assertThat(clock.now())
.isEqualTo(ClockTestUtil.createTimestamp(4, 100 * ClockTestUtil.NANOS_PER_MILLI));
assertThat(clock.now()).isEqualTo(4_100_000_000L);
}
@Test
public void measureElapsedTime() {
TestClock clock = TestClock.create(ClockTestUtil.createTimestamp(10, 1));
long nanos1 = clock.nowNanos();
clock.setTime(ClockTestUtil.createTimestamp(11, 5));
long nanos2 = clock.nowNanos();
assertThat(nanos2 - nanos1).isEqualTo(ClockTestUtil.NANOS_PER_SECOND + 4);
TestClock clock = TestClock.create(10_000_000_001L);
long nanos1 = clock.nanoTime();
clock.setTime(11_000_000_005L);
long nanos2 = clock.nanoTime();
assertThat(nanos2 - nanos1).isEqualTo(1_000_000_004L);
}
}

View File

@ -17,9 +17,7 @@
package io.opentelemetry.sdk.internal;
import static com.google.common.truth.Truth.assertThat;
import static io.opentelemetry.sdk.internal.ClockTestUtil.createTimestamp;
import io.opentelemetry.sdk.common.Timestamp;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
@ -27,35 +25,35 @@ import org.junit.runners.JUnit4;
/** Unit tests for {@link TimestampConverter}. */
@RunWith(JUnit4.class)
public class TimestampConverterTest {
private final Timestamp timestamp = createTimestamp(1234, 5678);
private final TestClock testClock = TestClock.create(timestamp);
private final long epochNanos = 1234_000_005_678L;
private final TestClock testClock = TestClock.create(epochNanos);
@Test
public void now() {
assertThat(testClock.now()).isEqualTo(timestamp);
assertThat(testClock.now()).isEqualTo(epochNanos);
TimestampConverter timeConverter = TimestampConverter.now(testClock);
assertThat(timeConverter.convertNanoTime(testClock.nowNanos())).isEqualTo(timestamp);
assertThat(timeConverter.convertNanoTime(testClock.nanoTime())).isEqualTo(epochNanos);
}
@Test
public void convertNanoTime_Positive() {
TimestampConverter timeConverter = TimestampConverter.now(testClock);
assertThat(timeConverter.convertNanoTime(testClock.nowNanos() + 3210))
.isEqualTo(createTimestamp(1234, 8888));
assertThat(timeConverter.convertNanoTime(testClock.nowNanos() + 1000))
.isEqualTo(createTimestamp(1234, 6678));
assertThat(timeConverter.convertNanoTime(testClock.nowNanos() + 15_999_994_322L))
.isEqualTo(createTimestamp(1250, 0));
assertThat(timeConverter.convertNanoTime(testClock.nanoTime() + 3210))
.isEqualTo(1234_000_008_888L);
assertThat(timeConverter.convertNanoTime(testClock.nanoTime() + 1000))
.isEqualTo(1234_000_006_678L);
assertThat(timeConverter.convertNanoTime(testClock.nanoTime() + 15_999_994_322L))
.isEqualTo(1250_000_000_000L);
}
@Test
public void convertNanoTime_Negative() {
TimestampConverter timeConverter = TimestampConverter.now(testClock);
assertThat(timeConverter.convertNanoTime(testClock.nowNanos() - 3456))
.isEqualTo(createTimestamp(1234, 2222));
assertThat(timeConverter.convertNanoTime(testClock.nowNanos() - 1000))
.isEqualTo(createTimestamp(1234, 4678));
assertThat(timeConverter.convertNanoTime(testClock.nowNanos() - 14000005678L))
.isEqualTo(createTimestamp(1220, 0));
assertThat(timeConverter.convertNanoTime(testClock.nanoTime() - 3456))
.isEqualTo(1234_000_002_222L);
assertThat(timeConverter.convertNanoTime(testClock.nanoTime() - 1000))
.isEqualTo(1234_000_004_678L);
assertThat(timeConverter.convertNanoTime(testClock.nanoTime() - 14000005678L))
.isEqualTo(1220_000_000_000L);
}
}

View File

@ -64,8 +64,8 @@ public class RecordEventsReadableSpanTest {
private final SpanId parentSpanId = TestUtils.generateRandomSpanId();
private final SpanContext spanContext =
SpanContext.create(traceId, spanId, TraceFlags.getDefault(), Tracestate.getDefault());
private final Timestamp startTime = Timestamp.create(1000, 0);
private final TestClock testClock = TestClock.create(startTime);
private final long startEpochNanos = 1000_123_789_654L;
private final TestClock testClock = TestClock.create(startEpochNanos);
private final TimestampConverter timestampConverter = TimestampConverter.now(testClock);
private final Resource resource = Resource.getEmpty();
private final Map<String, AttributeValue> attributes = new HashMap<>();
@ -101,8 +101,8 @@ public class RecordEventsReadableSpanTest {
Collections.<SpanData.TimedEvent>emptyList(),
Collections.singletonList(link),
SPAN_NAME,
Timestamp.create(startTime.getSeconds(), 0),
Timestamp.create(startTime.getSeconds(), 0),
Timestamp.fromNanos(startEpochNanos),
Timestamp.fromNanos(startEpochNanos),
Status.OK);
}
@ -121,7 +121,7 @@ public class RecordEventsReadableSpanTest {
SpanData spanData = span.toSpanData();
SpanData.TimedEvent timedEvent =
SpanData.TimedEvent.create(
Timestamp.create(startTime.getSeconds() + 1, 0),
Timestamp.fromNanos(startEpochNanos + NANOS_PER_SECOND),
"event2",
Collections.<String, AttributeValue>emptyMap());
verifySpanData(
@ -130,8 +130,8 @@ public class RecordEventsReadableSpanTest {
Collections.singletonList(timedEvent),
Collections.singletonList(link),
SPAN_NEW_NAME,
Timestamp.create(startTime.getSeconds(), 0),
Timestamp.create(testClock.now().getSeconds(), 0),
Timestamp.fromNanos(startEpochNanos),
Timestamp.fromNanos(testClock.now()),
Status.OK);
} finally {
span.end();
@ -150,7 +150,7 @@ public class RecordEventsReadableSpanTest {
SpanData spanData = span.toSpanData();
SpanData.TimedEvent timedEvent =
SpanData.TimedEvent.create(
Timestamp.create(startTime.getSeconds() + 1, 0),
Timestamp.fromNanos(startEpochNanos + NANOS_PER_SECOND),
"event2",
Collections.<String, AttributeValue>emptyMap());
verifySpanData(
@ -159,8 +159,8 @@ public class RecordEventsReadableSpanTest {
Collections.singletonList(timedEvent),
Collections.singletonList(link),
SPAN_NEW_NAME,
Timestamp.create(startTime.getSeconds(), 0),
Timestamp.create(testClock.now().getSeconds(), 0),
Timestamp.fromNanos(startEpochNanos),
Timestamp.fromNanos(testClock.now()),
Status.CANCELLED);
}
@ -225,12 +225,10 @@ public class RecordEventsReadableSpanTest {
RecordEventsReadableSpan span = createTestSpan(Kind.INTERNAL);
try {
testClock.advanceMillis(MILLIS_PER_SECOND);
long elapsedTimeNanos1 =
(testClock.now().getSeconds() - startTime.getSeconds()) * NANOS_PER_SECOND;
long elapsedTimeNanos1 = testClock.now() - startEpochNanos;
assertThat(span.getLatencyNs()).isEqualTo(elapsedTimeNanos1);
testClock.advanceMillis(MILLIS_PER_SECOND);
long elapsedTimeNanos2 =
(testClock.now().getSeconds() - startTime.getSeconds()) * NANOS_PER_SECOND;
long elapsedTimeNanos2 = testClock.now() - startEpochNanos;
assertThat(span.getLatencyNs()).isEqualTo(elapsedTimeNanos2);
} finally {
span.end();
@ -242,8 +240,7 @@ public class RecordEventsReadableSpanTest {
RecordEventsReadableSpan span = createTestSpan(Kind.INTERNAL);
testClock.advanceMillis(MILLIS_PER_SECOND);
span.end();
long elapsedTimeNanos =
(testClock.now().getSeconds() - startTime.getSeconds()) * NANOS_PER_SECOND;
long elapsedTimeNanos = testClock.now() - startEpochNanos;
assertThat(span.getLatencyNs()).isEqualTo(elapsedTimeNanos);
testClock.advanceMillis(MILLIS_PER_SECOND);
assertThat(span.getLatencyNs()).isEqualTo(elapsedTimeNanos);
@ -384,7 +381,7 @@ public class RecordEventsReadableSpanTest {
for (int i = 0; i < maxNumberOfEvents; i++) {
SpanData.TimedEvent expectedEvent =
SpanData.TimedEvent.create(
Timestamp.create(startTime.getSeconds() + maxNumberOfEvents + i, 0),
Timestamp.fromNanos(startEpochNanos + (maxNumberOfEvents + i) * NANOS_PER_SECOND),
"event2",
Collections.<String, AttributeValue>emptyMap());
assertThat(spanData.getTimedEvents().get(i)).isEqualTo(expectedEvent);
@ -397,7 +394,7 @@ public class RecordEventsReadableSpanTest {
for (int i = 0; i < maxNumberOfEvents; i++) {
SpanData.TimedEvent expectedEvent =
SpanData.TimedEvent.create(
Timestamp.create(startTime.getSeconds() + maxNumberOfEvents + i, 0),
Timestamp.fromNanos(startEpochNanos + (maxNumberOfEvents + i) * NANOS_PER_SECOND),
"event2",
Collections.<String, AttributeValue>emptyMap());
assertThat(spanData.getTimedEvents().get(i)).isEqualTo(expectedEvent);
@ -528,17 +525,17 @@ public class RecordEventsReadableSpanTest {
attributes,
links,
1);
long startTimeNanos = clock.nowNanos();
long startTimeNanos = clock.nanoTime();
clock.advanceMillis(4);
long firstEventTimeNanos = clock.nowNanos();
long firstEventTimeNanos = clock.nanoTime();
readableSpan.addEvent("event1", event1Attributes);
clock.advanceMillis(6);
long secondEventTimeNanos = clock.nowNanos();
long secondEventTimeNanos = clock.nanoTime();
readableSpan.addEvent("event2", event2Attributes);
clock.advanceMillis(100);
readableSpan.end();
long endTimeNanos = clock.nowNanos();
long endTimeNanos = clock.nanoTime();
SpanData expected =
SpanData.newBuilder()