-
Notifications
You must be signed in to change notification settings - Fork 345
Simple layer on top of ByteBuffer for BINARY format. #276
Simple layer on top of ByteBuffer for BINARY format. #276
Conversation
Provide a simple layout to let users get/set the used ByteBuffer, as well as hint them about the ByteBuffer length in the case of injection (as opposed as the current approach, where they need to pass a large-enough ByteBuffer when calling Tracer.inject()).
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 seems like a reasonable compromise. It would be interesting to try writing an instrumentation example that pools the buffers to minimize the allocations, as it seems like w a lot of objects are being created during inject/extract.
|
||
import java.nio.ByteBuffer; | ||
|
||
public final class Adapters { |
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.
what is the benefit of putting helpers here instead of BinaryAdapters.injectCarrier()
?
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 remember we had this idea of putting all the adapters in a single place (including the TextMap
ones) - else, I can definitely make BinaryAdapters
public, and remove the Adapters
class.
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 don't mind a shared class, but I'm bothered by the name "injectBinary", it reads like a command. We should come up with a more intuitive name, eg injectionCarrier, which works better with BinaryAdapters class.
|
||
private BinaryAdapters() {} | ||
|
||
public static class BinaryExtractAdapter implements Binary { |
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.
does it have to be public?
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.
In the current way, yes. But we can hide them through a single method (as we do now for Adapters
;) )
binary.injectBuffer().put(buff); | ||
|
||
} catch (IOException e) { | ||
throw new RuntimeException("Corrupted state"); |
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.
should wrap the cause
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.
Makes sense. Will do.
while (objStream.available() > 0) { | ||
baggage.put(objStream.readUTF(), objStream.readUTF()); | ||
} | ||
} catch (IOException e) { |
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.
rethrow as wrapped?
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.
So I'm wondering about this one - the TEXT_MAP
format returns nothing (null
) if the context is malformed (i.e. trace_id
but no span_id
, or nothing at all, etc). Should we re-throw the Exception
anyway here, though?
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 think for mock tracer it's better to throw on malformed carrier. Null is only when http header is missing (which doesn't apply to binary codecs).
} | ||
} | ||
} else { | ||
throw new IllegalArgumentException("Unknown carrier"); |
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 would move this to the top to reduce nesting
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.
Will do as well.
} | ||
} | ||
} else { | ||
throw new IllegalArgumentException("Unknown carrier"); |
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 would move this to the top to reduce nesting. Also clarify "expecting Binary, received " + carrier.getClass()
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.
Agreed ;)
The code was updated to show the suggested improvements @yurishkuro Let me know ;) |
* | ||
* @return The buffer used for SpanContext injection. | ||
*/ | ||
ByteBuffer injectBuffer(); |
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.
maybe injectionBuffer
to mirror injectionCarrier
in the adapter?
@yurishkuro Good point (on the Updated. |
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.
lgtm. I wonder if it would make sense to keep backwards compatibility and not change the BINARY
format but deprecated it and introduce a new one. Do we know if people are using the current BINARY?
@yurishkuro My impression is that nobody has been really using this API so far - still, I think it would be nice to ask on Gitter (at least), in case anybody was (if that's the case, we should definitely deprecate what we have now, etc). |
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 believe this interface puts
The previous interface where ByteBuffers were passed to the carrier did solve the first 2 items. What do you think ? |
@felixbarny Oh, if Instana is using On @raphw and his feedback, I'd suggest reading, for historical context:
I had previously in the same thread mentioned the advantages and disadvantages of what the options we had at hand - which leads us to have this simple approach (which I mentioned later on as well):
I'd urge you to read that to understand the full picture, so the situation is clear, and why we will go with this very PR for the time being. Else, let us know on your take (either for this PR still, or a future streaming based API ;) ) |
@hypnoce Hey, As the other questions (will write my answers here, but it sounds we should put them in the docs as well ;) )
Related to the previous point - and users dont have to handle the lifecycle of a class CustomBinary implements Binary
{
public void setInjectionBufferLength(int length)
{
this.buffer = CustomByteBufferPool.getWithLength(length);
}
public ByteBuffer injectionBuffer()
{
return this.buffer;
}
}
Implementing an extra method is less work than implementing, for users, a full Byte buffer = ...;
SpanContext ctx = tracer.extract(BinaryAdapters.extractionCarrier(buffer)); For the third point, see my answer to @felixbarny on why we decided to have that approach (streaming) as a separate, potential new FORMAT for the future.
Which one? #252? I think that's a fine API too, but this is simpler and would work just fine for simpler cases. |
Glad to see interest in this picking up! I'm pinging more people to make them aware of this thread. In the meantime, I would strongly suggest moving to tests and runnable code examples for illustrating various scenarios/points-of-concern. The ability for english/prose to move these discussions forwards becomes very limited once we get down into the weeds. |
I would also like to suggest that there may be no "universal" binary solution that would satisfy every case effectively. In Java for example, we may want one solution for situations that require a ByteBuffer, and a separate solution for streaming, rather than a single solution. If a single underlying solution can be made efficient, with higher-level utilities being employed to make it ergonomic for various scenarios, that would be great. But if that seems too difficult, consider that we could instead have several separate solutions. |
@carlosalberto Thanks for your answer !
Each middleware instrumenter would have an implementation of a ByteBuffer pool instead of the tracer implementation to handle it. Therefore, since a single JVM may host different middlewares but a single tracer implementation, you may endup using many ByteBuffer pools with variable degrees of stability.
Totally agree
Indeed, when the ByteBuffer is supplied by the instrumenter, then we need such method. When the BB is supplied by the tracer, then I believe this method disappears.
Exactly. In what aspect do you think the new proposed one is simpler ? In the end, I would emphasise that java.nio package is not asynchronous IO. It's New-IO and therefore does not enforce asynchronicity. Therefore, exposing ByteBuffer makes total sense in case of blocking IO. Thanks for your great work ! |
Hey @hypnoce, thanks for the message.
I think this one is barely a thin layer on top of the current approach (a bare
Not sure Tracer implementation is the best place to put this, and not sure they would love it ;) To me it sounds we should provide some import io.opentracing.contrib.BinaryPool;
// returns an object providing a ByteBuffer in the pool, based on a given length.
Binary binary = binaryPool.getInstance();
// Do the actual injection.
tracer.inject(ctx, Format.BINARY, binary);
// Use the result (provided by the pool)
ByteBuffer buffer = binary.injectionBuffer();
buffer.rewind();
request.setPayload(buffer); This way, we provide a safe place to start - and later people can use their own implementation if they need to. Thing this would work for you? Let us know ;) |
Hey @CodingFabian I've got a question for you ;) Is Instana is using the current Thanks in advance! |
well some of our implementations support it, not sure if any user uses it. |
@carlosalberto thanks for your answer I'm still not really confortable with the Maye I would rather replace it by an overloading of public interface Binary {
ByteBuffer injectionBuffer();
ByteBuffer injectionBuffer(int length);
ByteBuffer extractionBuffer();
} I beleive it makes the Binary interface easier to understand just by reading the methods. Also, It does not impose some ordering constraints on the calls. What do you think ? |
Hey! So I had previously played with such approach (
IHMO this is confusing - one of them is used for creation, and another one as a getter, but with the same name... I'd rather stick with (as mentioned):
And the user could access the underlying buffer depending on the actual implementation they have ( (In this case we could remove the I'm fine with this slight change - let me know what you think about this one. cc @yurishkuro ;) |
@CodingFabian Hey, thanks for the answer :) So I'd personally prefer to break the |
as long its documented, I would guess the few users using it would find the doc. |
@carlosalberto Do you think it's a good thing to document thrown exceptions or it's implementation dependant ? Thanks ! |
@hypnoce Oh, definitely we should document the exceptions (in the Will update the PR in the next few days. Thanks for the feedback! |
There's still room to change this once it's merged into the release candidate branch. But I would like to get feedback from @felixbarny and @raphw before doing so, to make sure their concerns have been addressed. Maybe a call in CET time would be quicker? |
I am currently on vacation and cannot follow this up but my concerns are the same as I initially mentioned. The current API would require to manage byte buffer pools both by the tracer and the user of a tracer. Also, the buffers must be sized by the required bytes for the trace information what makes reuse difficult. |
Hey @raphw
The current API (which simply uses a But as Ted mentioned, I think it would be nice to have an actual call to talk about a binary, stream based, more complex format (separated, such as |
Simply pass the length to injectionBuffer() directly, and refactor BinaryAdapters to wrap a ByteBuffer for injection (instead of creating a new one).
@hypnoce Hey, I have updated the PR to remove |
Hey @carlosalberto the changes look good to me. Thanks ! |
Hey @raphw @felixbarny before going ahead and merge this PR, we would like do cover this issue/PR up in the next Cross-Language group call on July 13th (next week). Observe the initial plan is to have this as the simple way to do binary propagation, but we would like to get some feedback (part of that is to work on some more advanced |
So I think we will be merging this onto the @raphw if you are still around, would be nice to schedule a Cross-Language call to talk about a more advanced @CodingFabian Final take on whether we could replace the existing |
@carlosalberto i am fine with breaking as we did not implement this anyway. Thats my vendor perspective. |
Hey @CodingFabian thanks for the answer.
You mean the current or this proposed one? ;) |
Hey all, Trying to get revive this PR - so essentially this approach is simply enough, yet handles an important case (telling the user in advance the required size of the buffer). In addition, I'd like to address that while prototyping Cassandra server side support, I saw the payload is received as a straight The arguments in favor we had before were: keeping it as a simply, yet more realistic approach to the one we have now; and against it, probably the need of a more advanced format (which could be addressed by a 'streaming' one in the future). Thoughts? |
Hey @CodingFabian In case we decide to merge this PR for doing a RC to test out a few new features, I'm wondering if Instana needs this new format to be exposed with a different name, in order to not break things for your Java client? Let me know ;) |
Hey all, I'd like to merge this PR by tomorrow, in order to include it in an incoming RC - so feel free to provide any feedback in case you think it needs tuning or shouldn't be merged even ;) (And my understanding, upon re-reading the conversation, is that it's fine to break Instana' BINARY tracer support ;) ) |
Merged this PR - feel free to open an issue if you feel something needs tuning (I will be putting an eye on that, and will update accordingly) Thanks all for your feedback! |
* When Binary is defined as inbound, extractionBuffer() will be called to retrieve the ByteBuffer | ||
* containing the data used for SpanContext extraction. | ||
* | ||
* When Binary is defined as outbound, setInjectBufferLength() will be called in order to hint |
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.
There is no setInjectBufferLenght() method defined in the interface, so it seems to me the documentation does not match the implementation 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.
Oh, definitely. Will update, thanks for the observation.
* Deprecate the StringTag.set() overload taking a StringTag. (#262) * Implement Trace Identifiers. (#280) * Bump JaCoCo to use a Java 10 friendly version (#306) * Remove finish span on close (#301) * Deprecate finishSpanOnClose on activation. * Add ScopeManager.activeSpan() and Tracer.activateSpan(). * Clarify the API changes and deprecations. * Add an error reporting sample to opentracing-testbed. * Simple layer on top of ByteBuffer for BINARY format. (#276) * Add generic typed setTag/withTag (#311) * Allow injecting into maps of type Map<String,Object> (#310) * Add simple registerIfAbsent to global tracer (#289) * Split Inject and Extract Builtin interfaces (#316) * Deprecate ScopeManager.active() (#326) * Make Tracer extends Closable. (#329) * Do not make isRegistered() synchronized. (#333) * Deprecate AutoFinishScopeManager (#335)
Provide a simple layout to let users get/set the used ByteBuffer,
as well as hint them about the ByteBuffer length in the case
of injection (as opposed as the current approach, where they
need to pass a large-enough ByteBuffer when calling
Tracer.inject()
).Observe this is merely a thin layer on top of
ByteBuffer
, but with the hint for letting the users know the required length of the buffer used byinject()
.