New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add WithClock TracerProviderOption #2052
base: main
Are you sure you want to change the base?
Conversation
Codecov Report
@@ Coverage Diff @@
## main #2052 +/- ##
=====================================
Coverage 76.0% 76.0%
=====================================
Files 174 174
Lines 12088 12105 +17
=====================================
+ Hits 9187 9204 +17
Misses 2656 2656
Partials 245 245
|
- change `SetClock` to `WithClock` style option - `Since` fallback to use `Now().Sub` if not implemented
Thanks for all the suggestions & comments. Code has been updated. |
- move MonotonicEndTime from sdk/internal to sdk/trace, and unexport it - use a struct instead of interface as traceproviders's real clock field
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be good if someone takes another look at the "godoc" comments.
Nice work 👍
Nice to have: it might be also good to create a benchmark to see how much time+memory we lose when creating start/end span timestamps because of the new clock abstractions.
godoc & others updated. default clockOne is to compare the default clock against original implementation ( pkg: go.opentelemetry.io/otel/sdk/trace
cpu: Intel(R) Core(TM) i7-7700 CPU @ 3.60GHz
BenchmarkAttributesMapToKeyValue-8 532833 2194 ns/op
BenchmarkStartEndSpan/AlwaysSample-8 1380988 869.2 ns/op 848 B/op 9 allocs/op
BenchmarkSpanWithAttributes_4/AlwaysSample-8 824190 1444 ns/op 1584 B/op 17 allocs/op
BenchmarkSpanWithAttributes_8/AlwaysSample-8 618087 1936 ns/op 2112 B/op 23 allocs/op
BenchmarkSpanWithAttributes_all/AlwaysSample-8 784983 1794 ns/op 1936 B/op 21 allocs/op
BenchmarkSpanWithAttributes_all_2x/AlwaysSample-8 395304 3125 ns/op 3236 B/op 32 allocs/op
PASS
ok go.opentelemetry.io/otel/sdk/trace 21.784s original (main branch): pkg: go.opentelemetry.io/otel/sdk/trace
cpu: Intel(R) Core(TM) i7-7700 CPU @ 3.60GHz
BenchmarkAttributesMapToKeyValue-8 501423 2167 ns/op
BenchmarkStartEndSpan/AlwaysSample-8 1429974 859.0 ns/op 848 B/op 9 allocs/op
BenchmarkSpanWithAttributes_4/AlwaysSample-8 965439 1473 ns/op 1584 B/op 17 allocs/op
BenchmarkSpanWithAttributes_8/AlwaysSample-8 594805 1935 ns/op 2112 B/op 23 allocs/op
BenchmarkSpanWithAttributes_all/AlwaysSample-8 638281 1812 ns/op 1936 B/op 21 allocs/op
BenchmarkSpanWithAttributes_all_2x/AlwaysSample-8 399723 3103 ns/op 3236 B/op 32 allocs/op
PASS
ok go.opentelemetry.io/otel/sdk/trace 20.962s custom clock which add "offset"The other check is to run against a custom clock in another repo (which is the original motivation that we want to add this clock abstraction). This clock has an Here is a snippet of the test logic (modified from func tracerWithNTPClock(b *testing.B, name string, sampler sdktrace.Sampler) trace.Tracer {
// clock initialization
clock := ...
tp := sdktrace.NewTracerProvider(sdktrace.WithSampler(sampler), sdktrace.WithClock(&clock))
return tp.Tracer(name)
}
func traceBenchmark(b *testing.B, name string, fn func(*testing.B, trace.Tracer)) {
b.Run("AlwaysSample", func(b *testing.B) {
b.ReportAllocs()
fn(b, tracer(b, name, sdktrace.AlwaysSample()))
})
b.Run("AlwaysSampleWithNTPClock", func(b *testing.B) {
b.ReportAllocs()
fn(b, tracerWithNTPClock(b, name, sdktrace.AlwaysSample()))
})
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just leaving a few nit comments
Approving ✔️
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This PR is already in a very nice shape 👍
Co-authored-by: Robert Pająk <pellared@hotmail.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is looking close.
tracer := tp.Tracer("custom-clock") | ||
|
||
_, span := tracer.Start(context.Background(), "test-frozen-clock") | ||
time.Sleep(time.Microsecond * 2) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This doesn't look needed. Is there a reason to pause here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IIRC in windows if a span ends too fast it can result in the same start time/end time. If that happens then the Equal
test would appears not that useful here.
sdk/trace/provider.go
Outdated
// to generate span start/end time. Clock.Stopwatch should start and | ||
// return a Stopwatch instance. For Stopwatch implementation, | ||
// Stopwatch.Started should return the time.Time when the stopwatch | ||
// started. Stopwatch.Elapsed should return the time.Duration measuring | ||
// the elapsed time from when the stopwatch started. Its value should be | ||
// positive to ensure monotonic start/end time of the span. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: It seems like this portion of the docs could be moved to their respective types/
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated. I feels like the existing types' comment basically says the same thing so I remove most of the docs here.
sdk/trace/span.go
Outdated
startTime = time.Now() | ||
span.stopwatch = tr.provider.clock.Stopwatch() | ||
} else { | ||
span.stopwatch = standardStopwatch(startTime) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the user supplies their own start time it shouldn't mean the user supplied clock is not used. This seems like a design flaw.
Should we updated the Clock
interface's Stopwatch
method to accept a start time?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess a given startTime
might not always be useful or reasonable for some clock implementations (but I cannot really come up with any realistic examples). And I feels like this would complicate many implementations that could have been straight forward. Besides if the user provides a startTime
and an endTime
then the custom clock would not be used anyways.
Considering a "offset" clock, if user provide startTime
, should the clock giving out the endTime
based on the given startTime
?
I added a parameter to clock's I also thought about adding a similar parameter to Current benchmark (3 rounds each branch):
clock branch (which is the default clock):
Since there were some refactors happened on the main branch, a more thorough review might be necessary in case I missed something important. |
Sorry for the long pause from last update. We still think this feature might be useful in some cases (although to be honest not very useful to most people). BTW this is the current benchmark for a comparison before & after this PR: main:
clock:
|
details please see #2043