opentelemetry-java icon indicating copy to clipboard operation
opentelemetry-java copied to clipboard

JavaVersionSpecific returns millisecond precision for currentTimeNanos()

Open scottsue opened this issue 1 year ago • 7 comments

From a Java 8 JVM perspective there are limitations to the precision of time, however looking at the implementation of JavaVersionSpecific.currentTimeNanos() for Java 8, this should return as close as possible, the nanosecond precision since epoch, however it is currently only to millisecond precision.

Steps to reproduce Create two Spans in the following order

  1. Create and start Span 1 - startEpochNanos is set to millisecond precision
  2. End Span 1 - endEpochNanos is set to nanosecond precision
  3. Create and start Span 2 - startEpochNanos is set to millisecond precision
  4. End Span 2 - endEpochNanos is set to nanosecond precision

It looks like a new AnchoredClock is created for each Span if it is not inheriting from a parentSpan, therefore if the spans are created in less than 1 millisecond of each other, there is a potential the Span1.endEpochNanos is greater than Span2.startEpochNanos

What did you expect to see? I would expect Span.startEpochNanos to be at nanosecond precision

What did you see instead? Span.startEpochNanos is at millisecond precision

What version and what artifacts are you using? Artifacts: opentelemetry-sdk-common Version: 1.35

Environment OS: Centos Runtime: Java 8 OpenJdk

scottsue avatar Feb 16 '24 00:02 scottsue

@scottsue I'm not aware of any reliable way of getting nanosecond precision wall time on Java 8. Do you know of something?

breedx-splk avatar Feb 17 '24 00:02 breedx-splk

@breedx-splk correct, unfortunately not a reliable way. However AnchoredClock does look to attempt to get a close approximation of this. So my thoughts was to perform something similar when calculating Span.startEpochNanos

scottsue avatar Feb 19 '24 10:02 scottsue

@breedx-splk correct, unfortunately not a reliable way. However AnchoredClock does look to attempt to get a close approximation of this. So my thoughts was to perform something similar when calculating Span.startEpochNanos

Would the idea then be to anchor to some arbitrary previous time then?

breedx-splk avatar Feb 20 '24 18:02 breedx-splk

I was just playing around to see how this could work and I created my own version of JavaVersionSpecific. The implementation requiring the following

private static final NanosecondClock SYSTEM_EPOCH_NANO_CLOCK = new NanosecondClock(null);

  public long currentTimeNanos() {
    return SYSTEM_EPOCH_NANO_CLOCK.getNowSinceEpochInNano();
  }

This utilises a static NanoClock which gives the ability to return the nanos since epoch. In essence, this is what AnchoredClock does as well, only that it seems AnchoredClock looks to be recreated if it is not inheriting from a parentSpan? I would say it is possibly feasible to have a static instance of AnchoredClock in my above implementation to give the same functionality?

scottsue avatar Feb 21 '24 12:02 scottsue

This utilises a static NanoClock which gives the ability to return the nanos since epoch.

Yeah, but you haven't shared the implementation of that class. Where is that from?

breedx-splk avatar Feb 21 '24 18:02 breedx-splk

The implementation of the NanosecondClock is something that I had to hand quickly internally. However after using this, I suspect that creating a static AnchoredClock could work.

scottsue avatar Feb 22 '24 09:02 scottsue

One way to get better than millisecond time on jdk8 would be to use jnr-ffi to call gettimeofday https://github.com/jnr/jnr-ffi-examples/blob/master/gettimeofday/src/main/java/gettimeofday/Gettimeofday.java We could add a SPI and bundle the jnr-ffi based time provider as separate jar so users who need it could enable it by including that jar in their project.

laurit avatar May 20 '24 12:05 laurit