Refactor for supporting external writer#35
Conversation
willgittoes-dd
left a comment
There was a problem hiding this comment.
It might help to see the "other half", since I'm not sure I understand the architecture here.
HTTPEncoder seems like it could be entirely static, and therefore replaced with a function.
| HttpEncoder() {} | ||
| virtual ~HttpEncoder() {} | ||
|
|
||
| virtual std::string path() = 0; |
| class SpanData; | ||
| using Trace = std::unique_ptr<std::vector<std::unique_ptr<SpanData>>>; | ||
|
|
||
| class HttpEncoder { |
| virtual ~HttpEncoder() {} | ||
|
|
||
| virtual std::string path() = 0; | ||
| virtual std::map<std::string, std::string> headers(std::deque<Trace> &traces) = 0; |
There was a problem hiding this comment.
And add a comment that describes the shape of this. From looking at the impl it looks idempotent, which is good! If we add a comment saying so, maybe it'll stay that way!
| std::string AgentHttpEncoder::path() { return agent_api_path; } | ||
|
|
||
| std::map<std::string, std::string> AgentHttpEncoder::headers(std::deque<Trace> &traces) { | ||
| std::map<std::string, std::string> headers(common_headers_); |
There was a problem hiding this comment.
This method could be static if only we got the 'version' into common_headers_ statically as well. Since version is known at compile time, surely there's some way we can statically reference it?
There was a problem hiding this comment.
Yeah. This was to satisfy an existing test which passed in an arbitrary version string and depended on its value later on.
I'll adjust the test and remove some bits.
| } | ||
|
|
||
| std::string AgentHttpEncoder::payload(std::deque<Trace> &traces) { | ||
| return trace_encoder_(std::move(traces)); |
There was a problem hiding this comment.
Why do we need to move? Why not just leave it owned by Writer (since this all happens synchronously), and let writer continue to explicitly clear() it and re-use the memory.
This should take a const reference to a Trace deque, and so should trace_encoder_.
There was a problem hiding this comment.
Yup. This was from rushing to implement. Will fix.
| namespace opentracing { | ||
|
|
||
| // A function that encodes a collection of traces. | ||
| typedef std::function<std::string(std::deque<Trace>)> TraceEncoder; |
There was a problem hiding this comment.
Will there be other implementations? Why not just call msgpack::pack directly in HttpEncoder::payload?
There was a problem hiding this comment.
Then we could make HttpEncoder::payload static, and then we don't even need a class. What does envoy need, can we just give them a function or something?
There was a problem hiding this comment.
The things envoy needs are more-or-less the three functions from HttpEncoder.
(We want to still control the implementation details of those, it just needs the data)
I'll tidy this up, make it simpler / flatter.
| ~AgentHttpEncoder() override {} | ||
|
|
||
| // Returns the path that is used to submit HTTP requests to the agent. | ||
| std::string path() override; |
| namespace datadog { | ||
| namespace opentracing { | ||
|
|
||
| Writer::Writer() : encoder_(std::unique_ptr<HttpEncoder>{new AgentHttpEncoder{}}) {} |
There was a problem hiding this comment.
Could we assign this in the class def?
There was a problem hiding this comment.
This is the one bit I wouldn't want to put in there.
It's the point where we set the bits we control for the writer that is extended by something else.
|
The "other half" is under development but depends on this change (that's also under development) 😄 |
190cb43 to
a24f4c1
Compare
a24f4c1 to
6425753
Compare
a5cfcec to
aa23cbc
Compare
willgittoes-dd
left a comment
There was a problem hiding this comment.
It's looking great! I'd set this to 'approve' with nitpicks, but I really want to see a diff of writer->agent_writer. So I've suggested you do a git mv (and then the writer.cpp as it exists in this PR is a "new file").
| }; | ||
|
|
||
| std::shared_ptr<ot::Tracer> makeTracer(const TracerOptions &options); | ||
| std::tuple<std::shared_ptr<ot::Tracer>, std::shared_ptr<TracePublisher>> makeTracerAndPublisher( |
There was a problem hiding this comment.
Might be good to add comments so anyone looking here would know why they'd want to use one method or the other.
| @@ -0,0 +1,173 @@ | |||
| #include "agent_writer.h" | |||
| #include <iostream> | |||
There was a problem hiding this comment.
Could we please undo this change, and redo it with git mv. Otherwise the history isn't kept and I can't see changes.
There was a problem hiding this comment.
I tried to fix this, but github isn't showing things clearly.
The history correctly shows up in blame, but not in the file's commit logs or in the PR diff
| @@ -0,0 +1,18 @@ | |||
| #include <datadog/opentracing.h> | |||
There was a problem hiding this comment.
Comment at the top about why we have two versions, and a comment on each about what they're for.
There was a problem hiding this comment.
Also remove this and re-add it using git mv opentracing.cpp opentracing_agent.cpp
| } | ||
| return std::move( | ||
| std::unique_ptr<ot::SpanContext>{new SpanContext{parent_id, trace_id, std::move(baggage)}}); | ||
| return std::unique_ptr<ot::SpanContext>{ |
There was a problem hiding this comment.
While we're fixing my silly mistakes, can we use make_unique here?
| void clearTraces() override; | ||
| // Returns the HTTP headers that are required for the collection of traces. | ||
| const std::map<std::string, std::string> headers() override; | ||
| // Returns the encoded payload from the collection of traces. |
There was a problem hiding this comment.
May as well put these comments in the superclass?
| std::unique_ptr<SpanData> makeSpanData(std::string type, std::string service, | ||
| ot::string_view resource, std::string name, | ||
| uint64_t trace_id, int64_t span_id, uint64_t parent_id, | ||
| uint64_t trace_id, uint64_t span_id, uint64_t parent_id, |
There was a problem hiding this comment.
Oh shiiiiiiii
I think I missed a -Wall somewhere.
There was a problem hiding this comment.
Even -Wall -Wextra -pedantic wouldn't have made noise about it.
| @@ -1,180 +1,14 @@ | |||
| #include "writer.h" | |||
| #include <iostream> | |||
There was a problem hiding this comment.
Yeah I think this diff/history would definitely make more sense if we git mv writer.cpp agent_writer.cpp and then make a new writer.cpp (and some for .h).
| @@ -1,6 +1,7 @@ | |||
| #include "../src/writer.h" | |||
There was a problem hiding this comment.
Yes, like this! It's been git mv 'd! And make a new writer_test.cpp that tests ExternalWriter (I know there's not much to test there, but still).
aa23cbc to
3c05488
Compare
3c05488 to
6a5d804
Compare
|
|
||
| // TracePublisher exposes the data required to publish traces to the | ||
| // Datadog Agent. | ||
| class TracePublisher { |
There was a problem hiding this comment.
Thinking about this, I'd want to call it 'TraceEncoder', since that's the piece of AgentWriter that's moved into here. Even the headers stuff makes sense as "encoding", since it's "encoding" the traces as HTTP.
| std::string operation_name_override = ""; | ||
| }; | ||
|
|
||
| // TracePublisher exposes the data required to publish traces to the |
There was a problem hiding this comment.
Needs a comment that it's not thread-safe. The way this is used in AgentWriter will work fine, and you should double-check that it's only going to be used single-threaded in the envoy stuff.
The best way to really make sure we don't fuck this up in the future, is to make the constructor private and have a static method return a unique_ptr only. That way, anyone who gets an instance of this object really has to go out of their way (dangerously get()ing the pointer) to use this in a multi-threaded context.
Related to some future proxy integrations, this lets external projects supply a Writer implementation for publishing traces.
This is useful when external projects need to control precisely when and how traces are published to the agent.
The Writer is given an HttpEncoder at initialization, and uses that to correctly publish the HTTP headers and msgpack-encoded payload to the agent. This allows us to retain control over those implementation details and not require the external project to provide an identical implementation.