Skip to content
This repository has been archived by the owner on Jul 1, 2022. It is now read-only.

Add BaggageRestrictionManager #217

Merged

Conversation

black-adder
Copy link
Contributor

@black-adder black-adder commented Jul 24, 2017

Subset of this: https://github.com/uber/jaeger-client-java/pull/210/files. Only adding the BaggageRestrictionManager abstract class and the DefaultBaggageRestrictionManager.

  • Tests

@codecov-io
Copy link

codecov-io commented Jul 24, 2017

Codecov Report

Merging #217 into master will decrease coverage by 0.13%.
The diff coverage is 94.33%.

Impacted file tree graph

@@             Coverage Diff              @@
##             master     #217      +/-   ##
============================================
- Coverage     81.53%   81.39%   -0.14%     
- Complexity      491      504      +13     
============================================
  Files            79       82       +3     
  Lines          1895     1935      +40     
  Branches        229      232       +3     
============================================
+ Hits           1545     1575      +30     
- Misses          253      261       +8     
- Partials         97       99       +2
Impacted Files Coverage Δ Complexity Δ
...src/main/java/com/uber/jaeger/metrics/Metrics.java 77.5% <ø> (ø) 11 <0> (ø) ⬇️
...eger/baggage/DefaultBaggageRestrictionManager.java 100% <100%> (ø) 3 <3> (?)
...in/java/com/uber/jaeger/baggage/BaggageSetter.java 100% <100%> (ø) 9 <9> (?)
...ger-core/src/main/java/com/uber/jaeger/Tracer.java 85.52% <100%> (+0.32%) 18 <2> (+1) ⬆️
...aeger-core/src/main/java/com/uber/jaeger/Span.java 80.8% <50%> (-4.1%) 37 <1> (-2)
...main/java/com/uber/jaeger/baggage/Restriction.java 66.66% <66.66%> (ø) 2 <2> (?)
...ain/java/com/uber/jaeger/senders/ThriftSender.java 73.46% <0%> (-4.09%) 10% <0%> (ø)
...jaeger/reporters/protocols/ThriftUdpTransport.java 77.96% <0%> (-3.39%) 14% <0%> (ø)
...java/com/uber/jaeger/reporters/RemoteReporter.java 87.14% <0%> (-2.86%) 9% <0%> (ø)
... and 2 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 ec6ac78...877120b. Read the comment docs.

Copy link
Member

@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.

Have you given any thought to my comment about minimizing memory allocations? For example, even if you need multi-dimensional answer from baggage manager, you can achieve that without returning a value object, by using a strategy pattern, i.e. returning an actor that "does something with key/value", which could be no-op & metrics, set & metrics, truncate & set & metrics. All those actors can be pre-created by the manager once.

}
SanitizedBaggage baggage = this.tracer.getBaggageRestrictionManager().sanitizeBaggage(key, value);
if (!baggage.isValid()) {
this.tracer.getMetrics().baggageUpdateFailure.inc(1);
Copy link
Member

Choose a reason for hiding this comment

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

might be useful to log this to span as well

@black-adder
Copy link
Contributor Author

I attempted the strategy pattern approach and it's a hacky mess. If you're concerned about minimizing memory allocations, maybe I can go back to my first ever approach where I only return (isValid, maxValueLength) which are created once (a minute) and cached.


String prevItem = span.getBaggageItem(key);
this.logFields(span, key, value, prevItem, truncated, false);
SpanContext context = span.context().withBaggageItem(key, value);
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 have to create the new context and pass it back given the setter isn't public. Also the BaggageValidity class reeks of feature envy. I could move this file down to the same namespace as Span and make the context setter protected or I could remove the functionality in this class completely and inline it in Span.setBaggageItem()

}
BaggageValidity baggageValidity = this.getTracer().getBaggageRestrictionManager().isBaggageValid(key, value);
Copy link
Member

@yurishkuro yurishkuro Jul 24, 2017

Choose a reason for hiding this comment

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

how about

  • s/BaggageValidity/BaggageRestriction/
  • s/isBaggageValid(key, value)/getBaggageRestriction(key)/
  • s/sanitizeBaggage/setBaggage/
  • move the call to getBaggageRestriction outside of synchronized since it's not dependent on the span

Copy link
Member

Choose a reason for hiding this comment

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

or s/BaggageValidity/BaggageSetter/ (back to "strategy" pattern)

final int maxValueLength;

public SpanContext sanitizeBaggage(Span span, String key, String value) {
Metrics metrics = span.getTracer().getMetrics();
Copy link
Contributor

Choose a reason for hiding this comment

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

I'd initialize the metrics object by accepting a Metrics when creating an instance of the class. There is no necessity to retrieve it once per method call.

See also: Law of Demeter

fields.put("override", "true");
}
return this.log(fields);
if (key == null || value == null) {
Copy link
Contributor

Choose a reason for hiding this comment

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

This test doesn't need to be synchronized.

private String serviceName;
private Clock clock = new SystemClock();
private Map<String, Object> tags = new HashMap<String, Object>();
private boolean zipkinSharedRpcSpan;
private ActiveSpanSource activeSpanSource = new ThreadLocalActiveSpanSource();
private BaggageRestrictionManager baggageRestrictionManager = new DefaultBaggageRestrictionManager();
private BaggageRestrictionManager baggageRestrictionManager =
new DefaultBaggageRestrictionManager(this.metrics);
Copy link
Contributor

Choose a reason for hiding this comment

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

This introduces a bug; the instance of metrics used by DefaultBaggageRestrictionManager would always be the one set in L453.

@@ -499,6 +508,9 @@ public Builder withZipkinSharedRpcSpan() {

public Builder withMetrics(Metrics metrics) {
this.metrics = metrics;
if (this.baggageRestrictionManager instanceof DefaultBaggageRestrictionManager) {
this.baggageRestrictionManager = new DefaultBaggageRestrictionManager(this.metrics);
}
Copy link
Contributor

@vprithvi vprithvi Jul 25, 2017

Choose a reason for hiding this comment

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

This is bad because withMetrics now has multiple responsibilities.
It is also incorrect in that it misses the case where metrics are initialized withStatsReporter

A better way to handle this is to leave baggageRestrictionManager uninitialized, and initialize it in build with the right metrics instance.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

dope

return context;
}

void logFields(Span span, String key, String value, String prevItem, boolean truncated, boolean invalid) {
Copy link
Contributor

Choose a reason for hiding this comment

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

This should be private.

final Metrics metrics;

public SpanContext setBaggage(Span span, String key, String value) {
if (!this.isValid()) {
Copy link
Contributor

Choose a reason for hiding this comment

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

It's simpler to check the boolean directly instead of using the getter

public SpanContext setBaggage(Span span, String key, String value) {
if (!this.isValid()) {
metrics.baggageUpdateFailure.inc(1);
this.logFields(span, key, value, null, false, true);
Copy link
Contributor

Choose a reason for hiding this comment

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

There is no need to use this here


package com.uber.jaeger.baggage;

public abstract class BaggageRestrictionManager {
Copy link
Contributor

Choose a reason for hiding this comment

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

Could you add javadocs on what this is?

final int maxValueLength;
final Metrics metrics;

public SpanContext setBaggage(Span span, String key, String value) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Add javadoc, simply based on the method name, I'd conclude that it simply sets baggage.

package com.uber.jaeger.baggage;

public abstract class BaggageRestrictionManager {
static final int DEFAULT_MAX_VALUE_LENGTH = 2048;
Copy link
Contributor

Choose a reason for hiding this comment

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

This is only used in the DefaultBaggageRestrictionManager. Do you see this being made use of elsewhere? Perhaps it can be moved to DefaultBaggageRestrictionManager

Copy link
Contributor Author

Choose a reason for hiding this comment

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

it'll be used in another manager in the follow up PR

}

@Override
public BaggageSetter getBaggageSetter(String key) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we need this?
Can't we use lombok's @Getter annotation?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

getBaggageSetter is an interface function

Copy link
Contributor

Choose a reason for hiding this comment

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

discussed this offline: my suggestion doesn't work because the getter requires a key

1L, metricsReporter.counters.get("jaeger.baggage-update.result=ok").longValue());
}

private void assertBaggageLogs(
Copy link
Contributor

Choose a reason for hiding this comment

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

It looks like testing concerns aren't separated between SpanTest and BaggageSetter.

Ideally, we would only do the following in SpanTest
Assert that span.setBaggageItem invokes BaggageSetter with the right arguments.
Assert that span.getBaggeItem can retrieve baggage that has been set

All of the detailed test cases that only involve setting baggage should be under the purview of BaggageSetterTest. This results in a clear separation of concerns and code deduplication.

@black-adder black-adder force-pushed the Add_BaggageRestrictionManager branch from af37bfb to 72c1b84 Compare July 26, 2017 18:28
public class BaggageSetter {
final boolean valid;
final int maxValueLength;
final Metrics metrics;
Copy link
Member

Choose a reason for hiding this comment

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

  • to make fields visible to subclasses, they should be protected. Did you also need package visibility?
  • given that these fields are final, there should be a constructor setting them. If they are only set by subclasses, then the constructor can be protected (but then this class should be abstract).
  • the concept of valid is not clear, please document

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@value(staticConstructor = "of") generates a private constructor and a public factory for this class.

return context;
}

private void logFields(Span span, String key, String value, String prevItem, boolean truncated, boolean invalid) {
Copy link
Member

Choose a reason for hiding this comment

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

avoid negatives - s/invalid/valid/

public SpanContext setBaggage(Span span, String key, String value) {
if (!valid) {
metrics.baggageUpdateFailure.inc(1);
logFields(span, key, value, null, false, true);
Copy link
Member

Choose a reason for hiding this comment

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

if you move prevItem and truncated declarations to the top, this could be made a lot more readable: logFields(span, key, value, prevValue, truncated, valid);

fields.put("truncated", "true");
}
if (invalid) {
fields.put("invalid", "true");
Copy link
Member

Choose a reason for hiding this comment

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

why are you logging these as strings instead of booleans?

Copy link
Member

Choose a reason for hiding this comment

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

ah, nvm

* This flag represents whether the key is a valid baggage key. If valid
* the baggage key:value will be written to the span.
*/
private final boolean valid;
Copy link
Contributor

Choose a reason for hiding this comment

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

If something is not a valid baggage key then why create a BaggageSetter for it?

Copy link
Member

Choose a reason for hiding this comment

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

I assume it would create a Noop setter. This way the validity does not need to be exposed outside of baggage manager into the Span


@Override
public BaggageSetter getBaggageSetter(String key) {
BaggageSetter setter = baggageSetters.get(key);
Copy link
Member

Choose a reason for hiding this comment

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

unsynchronized map

…e SpanSetter since this wont be able to scale with the number of random baggage people can set
@black-adder black-adder force-pushed the Add_BaggageRestrictionManager branch from 2aeba8f to 495a656 Compare July 28, 2017 14:50
@black-adder black-adder force-pushed the Add_BaggageRestrictionManager branch from fdfa73f to 72cbdc5 Compare August 8, 2017 21:13
@black-adder
Copy link
Contributor Author

Rewrote to fit into new API.

Copy link
Member

@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.

not clear how this will support per-service restrictions

/**
* Sets the baggage key:value on the {@link Span} and the corresponding
* logs. Whether the baggage is set on the span depends on if the key
* is valid.
Copy link
Member

Choose a reason for hiding this comment

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

s/is valid/is allowed to be set by this service/

}
return this.log(fields);
}
this.context = getTracer().setBaggage(this, key, value);
Copy link
Member

Choose a reason for hiding this comment

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

why getTracer instead of just tracer?

* BaggageRestrictionManager is an interface for a class that manages baggage
* restrictions for baggage keys. The manager will return a {@link Restriction}
* for a specific baggage key which will determine whether the baggage key is
* allowed and any other applicable restrictions on the baggage value.
Copy link
Member

Choose a reason for hiding this comment

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

s/is allowed/is allowed for the current service/

public interface BaggageRestrictionManager {
int DEFAULT_MAX_VALUE_LENGTH = 2048;

Restriction getRestriction(String key);
Copy link
Member

Choose a reason for hiding this comment

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

shouldn't we pass the service name?

@black-adder
Copy link
Contributor Author

I'm not sure how the whole client will support the per service use case. Maybe we should have a plan for that before making the baggagerestrictionmanager battle ready?

@yurishkuro
Copy link
Member

the service name will be associated with the span, not the tracer. But I see how in this version you filter on the service name at the time of querying the backend, so that's ok for now. But it will be an API change in the future, so it might make sense to just pass the service name from the tracer to baggage restriction manager, even if it ignores it for now.

@black-adder
Copy link
Contributor Author

just pass the service name from the tracer to baggage restriction manager (BRM)

I'm not sure if that's future proof. My assumption is that in this dystopian future with no rules, people will bring up a tracer without a service name which means we can't initialize the BRM with a service. This means that we have to change getRestriction(key) to getRestriction(service, key) and then have BRM asynchronously fill its cache with restrictions for the service.

  1. Looks like we'd need a batch endpoint to agent to retrieve restrictions for multiple services
  2. First call to getRestriction will always fail (unless we have a failClosed flag)

@black-adder black-adder merged commit a44f07f into jaegertracing:master Aug 10, 2017
@black-adder black-adder deleted the Add_BaggageRestrictionManager branch August 10, 2017 21:13
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants