Skip to content
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 Jaeger throttling config #233

Merged
merged 9 commits into from
Jun 11, 2018
Merged

Conversation

isaachier
Copy link
Contributor

No description provided.

@CLAassistant
Copy link

CLAassistant commented May 23, 2018

CLA assistant check
All committers have signed the CLA.

Signed-off-by: Isaac Hier <ihier@uber.com>
@codecov
Copy link

codecov bot commented May 24, 2018

Codecov Report

Merging #233 into dev will increase coverage by 0.42%.
The diff coverage is 84.61%.

Impacted file tree graph

@@            Coverage Diff             @@
##              dev     #233      +/-   ##
==========================================
+ Coverage   93.42%   93.84%   +0.42%     
==========================================
  Files          40       40              
  Lines        2721     2194     -527     
==========================================
- Hits         2542     2059     -483     
+ Misses        130       85      -45     
- Partials       49       50       +1
Impacted Files Coverage Δ
main.go 91.74% <84.61%> (-0.57%) ⬇️
templateargs/interpolate/parse.go 90.09% <0%> (-1.96%) ⬇️
thrift/const.go 94.28% <0%> (-0.72%) ⬇️
thrift/response.go 96.82% <0%> (-0.68%) ⬇️
peerprovider/interface.go 94.11% <0%> (-0.62%) ⬇️
peerprovider/parse.go 96.15% <0%> (-0.52%) ⬇️
sorted/mapkeys.go 100% <0%> (ø) ⬆️
ratelimit/time_period.go 100% <0%> (ø) ⬆️
limiter/limiter.go 100% <0%> (ø) ⬆️
unmarshal/unmarshal.go 100% <0%> (ø) ⬆️
... and 27 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 2f9bbfd...a290a0f. Read the comment docs.

Copy link

@black-adder black-adder left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

duck typing an interface in the internal directory of another repo might be dangerous but I don't see a better way to do this. The alternative is to mock the agent but that's mocking the dependency of a dependency which seems even weirder. TLDR; this is ok(?)

@isaachier
Copy link
Contributor Author

Ya I agree. @yurishkuro @vprithvi any thoughts about how to test debug trace throttling in this code without mocking Jaeger components here?

Copy link
Contributor

@prashantv prashantv left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the PR, I have a couple of questions:

  • Will this impact tracer creation of run on a machine that doesn't have the Jaeger agent? E.g., local developer laptops?
  • Why are credits floats? It seems like they're used as ints throughout.

@@ -89,13 +89,10 @@ func verifyBaggage(ctx context.Context) error {
return errors.New("missing span")
}

spanCtx, ok := span.Context().(jaeger.SpanContext)
_, ok := span.Context().(jaeger.SpanContext)
if !ok {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: can you inline the ok := into the if,

if _, ok := span.Context().(jaeger.SpanContext); !ok {
  [...]
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ya sure.

main.go Outdated
tracer, closer, err = jaeger_config.Configuration{
ServiceName: opts.TOpts.CallerName,
Throttler: &jaeger_config.ThrottlerConfig{
SynchronousInitialization: true,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you explain this a little -- i'm assuming it means we try to get the throttling information synchronously? if that fails (e.g., on machines without jaeger), what is the impact?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Synchronous initialization is required for scripting tools such as yab that will likely exist in a short-lived process. For a service, we can periodically fetch the credit information (i.e. every 10 seconds). That is not feasible in a short script, so we set this flag to indicate we need to request credits from the Jaeger agent synchronously if there are no credits available for a given operation (credits represent number of debug spans we can emit without violating the throttling API). On machines without Jaeger, this would fail the request and refuse to emit debug spans, which is fine for those machines without agents. I would have thought the initial query could take a while to determine that the agent is not running and return an error, but local tests indicate no significant lag.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@isaachier add this justification as a code comment.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea.

return getTestTracerWithCredits(serviceName, credits, nil)
}

func getTestTracerWithCredits(serviceName string, credits float64, t *testing.T) (opentracing.Tracer, io.Closer) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why is credits a float?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just because the rate limiting API uses a token bucket to increment the total... floats are easier to use when you have a constant rate over time. The spending amount is always 1.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changing this to ints for internal mocks.

return getTestTracerWithCredits(serviceName, credits, nil)
}

func getTestTracerWithCredits(serviceName string, credits float64, t *testing.T) (opentracing.Tracer, io.Closer) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

make t *testing.T the first argument

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense. Good idea.

func getTestTracer(serviceName string) (opentracing.Tracer, io.Closer) {
return jaeger.NewTracer(serviceName, jaeger.NewConstSampler(true), jaeger.NewNullReporter())
const (
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: no need for var group, just const credits = 10 is fine

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good to know. Didn't think about that.

t *testing.T
}

func newDebugTraceCounter(numDebugTraces int, t *testing.T) jaeger.Reporter {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

t *testing.T should be the first argument

}

func (d *debugTraceCounter) Report(span *jaeger.Span) {
if span == nil {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this actually possible?

Copy link
Contributor Author

@isaachier isaachier Jun 4, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doesn't seem to be. Will remove. I miss assert(span != nil) 😢.

func (d *debugTraceCounter) Close() {
d.Lock()
defer d.Unlock()
if d.t == nil {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will this ever happen?

Copy link
Contributor Author

@isaachier isaachier Jun 4, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same assertion pattern from above. Will remove.

Actually, this is the case if any test case calls getTestTracer instead of getTestTracerWithCredits. The former method does not take a *testing.T argument, and we hit this if statement upon tracer.Close().

creditsPerDebugTrace = 1
)
d.Lock()
defer d.Unlock()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: can you add a blank line after the defer

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure

@isaachier
Copy link
Contributor Author

Added some comments.

main.go Outdated
@@ -360,7 +361,27 @@ func getTracer(opts Options, out output) (opentracing.Tracer, io.Closer) {
closer io.Closer
)
if opts.TOpts.Jaeger && !opts.TOpts.NoJaeger {
tracer, closer = jaeger.NewTracer(opts.TOpts.CallerName, jaeger.NewConstSampler(true), jaeger.NewNullReporter())
// yab must set the `SynchronousInitialization` flag to indicate that
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment explaining SynchronousInitialization.

@isaachier
Copy link
Contributor Author

@prashantv anything else?

@@ -2,8 +2,8 @@ sudo: false
language: go

go:
- 1.8
- 1.9
- "1.9"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this required?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The 1.10 addition is needed for the new Zap that we use. Something about type aliasing there. If you are referring to the quotes, Travis explains that without quotes, 1.10 is a float, so it becomes 1.1.

main.go Outdated
// yab must set the `SynchronousInitialization` flag to indicate that
// the Jaeger client must fetch debug credits synchronously. In a
// short-lived process like yab, the Jaeger client cannot afford to
// postpone the credit request for later in time.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please add a note about what happens if this fails (e.g., that it will just not send spans).

the comment should make it clear that err != nil does not happen if we fail to get the credits synchronously

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure

main.go Outdated
// short-lived process like yab, the Jaeger client cannot afford to
// postpone the credit request for later in time.
var err error
tracer, closer, err = jaeger_config.Configuration{
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we move all of the logic (which is now no longer a simple line) into a separate function, so the comment + all the logic for synchronous initialization is in a single createJaegerTracer function

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

definitely


func (d *debugTraceCounter) Close() {
d.Lock()
defer d.Unlock()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: blank after defer

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will do

func getTestTracer(serviceName string) (opentracing.Tracer, io.Closer) {
return jaeger.NewTracer(serviceName, jaeger.NewConstSampler(true), jaeger.NewNullReporter())
const credits = 10
return getTestTracerWithCredits(nil, serviceName, credits)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there's only 2 callers of getTestTracer, i'd suggest changing it to take a *testing.T as the first argument, and propagating it, instead of the nil check you have above.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK if that is the entire usage, I am totally fine. I was under the impression there were more callers.

main.go Outdated
} else if len(opts.ROpts.Baggage) > 0 {
return createJaegerTracer(opts, out)
}
if len(opts.ROpts.Baggage) > 0 {
out.Fatalf("To propagate baggage, you must opt-into a tracing client, i.e., --jaeger")
}
return tracer, closer
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no need for vars at the beginning, you can just do:

return opentracing.NoopTracer{}, closer

Copy link
Contributor

@prashantv prashantv left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for addressing all my comments, @yurishkuro, does this look good to you as well?

main.go Outdated
if opts.TOpts.Jaeger && !opts.TOpts.NoJaeger {
tracer, closer = jaeger.NewTracer(opts.TOpts.CallerName, jaeger.NewConstSampler(true), jaeger.NewNullReporter())
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@prashantv why was yab using a null reporter? Do you have a specific need to omit the root span?

Copy link
Contributor Author

@isaachier isaachier Jun 8, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have just changed this based on @yurishkuro's advice. Unless you mind, we might as well do this all at once.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was going to suggest not making this discussion a part of this PR, but move to #234.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm going to undo this and potentially open a new PR about this.

// only enable the span when we have not hit the throttling
// threshold.
jaeger_config.Sampler(jaeger.NewConstSampler(false)),
jaeger_config.Reporter(jaeger.NewNullReporter()),
Copy link

@yurishkuro yurishkuro Jun 8, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think these two customizations are not necessary, but can be discussed separately - #234.

Copy link

@yurishkuro yurishkuro left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

@isaachier
Copy link
Contributor Author

@prashantv can you please merge this PR?

@prashantv prashantv merged commit 539a20d into yarpc:dev Jun 11, 2018
@isaachier isaachier deleted the jaeger-debug-throttling branch June 11, 2018 17:22
@isaachier
Copy link
Contributor Author

Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

5 participants