-
Notifications
You must be signed in to change notification settings - Fork 721
Add JDBI 3 plugin #1460
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 JDBI 3 plugin #1460
Conversation
f756d98
to
969af12
Compare
969af12
to
646c901
Compare
Rebased, bumped parent version, bumped jdbi version. |
646c901
to
ccc66d9
Compare
@codefromthecrypt any concerns from you? thanks! |
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 like the work, but I think we need to study a bit more other modules (or ask your favorite coding assistant to). Please use same conventions in coding and tests for the more recent similar code. you've made a good start though!
.addContextListener(new StatementContextListener() { | ||
@Override | ||
public void contextCreated(final StatementContext ctx) { | ||
String spanName = "jdbi." + ctx.describeJdbiStatementType(); |
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.
hmm try to match the data policy with mysql. I don't think we want to put jdbi in the span name.. https://github.com/openzipkin/brave/blob/master/instrumentation/mysql8/src/main/java/brave/mysql8/TracingQueryInterceptor.java
instrumentation/jdbi3/src/main/java/brave/jdbi3/Jdbi3BravePlugin.java
Outdated
Show resolved
Hide resolved
this(tracing, false); | ||
} | ||
|
||
public Jdbi3BravePlugin(Tracing tracing, boolean includeBindVariables) { |
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 you look elsewhere you'll see types that have builders. Typically we don't add public ctors with options as it is constant maintenance. maybe find one to copy/paste find/replace
import brave.handler.SpanHandler; | ||
import brave.propagation.TraceContext; | ||
|
||
class Jdbi3BravePluginIT { |
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.
try for naming conventions and other conventions in the project. IT prefix e.g. https://github.com/openzipkin/brave/blob/master/instrumentation/mysql8/src/test/java/brave/mysql8/ITTracingQueryInterceptor.java and also IT implies real DB. This might be a unit test? ack jdbc:h2:mem is grey area.
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´s the use of an actual (though in-process and in-memory) database, along with using the third-party libraries as-is (as opposed to stubbing them, or parts of them, out) that made me call it an IT, but if you disagree, I´ll gladly change it.
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.
Ah well you can probably use the mysql container we use in the other but agree it is a minor point. Just make sure the tests are similar as possible. Cheers
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´ve changed the tests over to more-or-less a copy of the tests from the mysql8 instrumentation. I´ll make a stab at getting the tests run with both H2 and MySQL, as a way to show that the instrumentation is not tied to any particular database.
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.
Made the test abstract, with concrete implementations that test against H2 in-memory, MySQL configured as in the mysql8 tests, Postgres via the Zonky.io extension, and Sqlite via the xerial.org library.
In Junit 5.13, there will be a cleaner way of doing this, but it´s not released yet.
ccc66d9
to
17321f1
Compare
Thank you for your comments! I believe I´ve addressed them all in 17321f1 |
17321f1
to
ae0492c
Compare
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.
last feedback!
Unless there is a main code difference, please test only one DB as an IT. We should save test time for things that improve coverage.
Finally, it is easy to miss, but brave-bom needs an update both version and also to include this module https://github.com/openzipkin/brave/blob/master/brave-bom/pom.xml#L12
ae0492c
to
f5fd953
Compare
Thank you for your patience and prompt reviews! I hope all should be in order now.
OK, dropped everything but the MySQL one.
Done. |
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 wanted to click approve, but really there are a lot of little things to clean up. clean up what you can. If you are out of time, then let's leave this PR open until it can be.
private boolean includeBindVariables = false; | ||
|
||
public Builder(Tracing tracing) { | ||
this.tracing = tracing; |
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.tracing = tracing; | |
if (tracing == null) throw new NullPointerException("tracing == null"); | |
this.tracing = tracing; |
return null; | ||
} | ||
|
||
public static class Builder { |
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.
public static class Builder { | |
public static final class Builder { |
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.
Not sure the builder is still needed now that includeBindVariables
is not there any longer, though.
} | ||
URI uri; | ||
try { | ||
uri = new URI(connection.getMetaData().getURL().replaceAll("^jdbc:", "")); |
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.
normally I would challenge you to make this similar to mysql code which is more efficient than regex. However, you've had a lot of back and forth so we can leave that for someone else to refine. Generally it is better to look at how we do things as often there is some performance mania going on, though not always.
brave/instrumentation/mysql/src/main/java/brave/mysql/TracingStatementInterceptor.java
Lines 76 to 77 in ff40062
URI url = URI.create(connection.getMetaData().getURL().substring(5)); // strip "jdbc:" | |
String remoteServiceName = connection.getProperties().getProperty("zipkinServiceName"); |
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.
Also added a simple cache, since a listener usually only ever sees one URL, and the remote service name for a given URL will always be the same.
* JDBI plugin that sends a trace span for every SQL command executed by Jdbi. | ||
* Install by calling {@code jdbi.installPlugin(new Jdbi3BravePlugin(tracing));} | ||
*/ | ||
public class Jdbi3BravePlugin extends JdbiPlugin.Singleton { |
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.
public class Jdbi3BravePlugin extends JdbiPlugin.Singleton { | |
public final class Jdbi3BravePlugin extends JdbiPlugin.Singleton { |
return this; | ||
} | ||
|
||
public Jdbi3BravePlugin build() { |
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.
always return the most narrow type you can that works. Either plugin or plugin.singleton depending on what you think here. This is likely a place where since mysql plugin was made so long ago we didn't make do this.
public Jdbi3BravePlugin build() { | |
public JdbiPlugin.Singleton build() { |
|
||
assertThat(spans) | ||
.extracting(MutableSpan::name) | ||
.containsExactly("Query"); |
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 you've misunderstood me. We shouldn't model differently than mysql. Please match your tests to what is here, as the span name shouldn't be "Query" that's not a SQL operator
brave/instrumentation/mysql8/src/test/java/brave/mysql8/ITTracingQueryInterceptor.java
Line 109 in ff40062
.containsExactly("select"); |
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.
That´s the reason I had the jdbi.
prefix originally - the name is the Jdbi description of the statement type, not the first word of the statement. Extracting the first word of the statement seems very brittle (e.g. it fails when using comment prefixes, which is generally is good practice), and since the full SQL is in the sql.query
tag, it is also redundant. Using the Jdbi description of the type of statement (e.g. Query, Update, or Call) seems to me to be a better way.
I can change it if you insist, but I do think it would result in a worse implementation.
|
||
@Override | ||
public void customizeJdbi(Jdbi jdbi) { | ||
if (tracing == null) { |
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.
remove tracing == null here to reduce branching, as it is a bug to have null here and we can guard against it.
* Instrumentation for JDBI 3. | ||
* <p> | ||
* JDBI plugin that sends a trace span for every SQL command executed by Jdbi. | ||
* Install by calling {@code jdbi.installPlugin(new Jdbi3BravePlugin(tracing));} |
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 drifted, also check your README
/** | ||
* Instrumentation for JDBI 3. | ||
* <p> | ||
* JDBI plugin that sends a trace span for every SQL command executed by Jdbi. |
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 sounds redundant. A lot of code here has had a lot of review, so you can avoid back and forth by copying it first, then revising. For example, this says what (a couple times) not why. e.g. if you look at the reference I mentioned, the javadoc says this A MySQL query interceptor that will report to Zipkin how long each query takes
So the why is about the query. Also, we don't need to get specific to the version as that's already in the package name.
instrumentation/jdbi3/pom.xml
Outdated
|
||
<main.basedir>${project.basedir}/../..</main.basedir> | ||
|
||
<embedded-postgres.version>2.1.0</embedded-postgres.version> |
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 there are a number of unused versions here. Also make sure you aren't declaring a version already declared elsewhere. I think you might be on mysql-connector-java.version
. If that version isn't in the top-level pom due to lack of re-use, I would extract the one from mysql8 up to the top-level pom, then use the same variable name here.
When we don't take care to match other instrumentation, it leaves little bombs for maintainers later. For example, @reta and I have been dealing with versions for many years on code that sometimes was only polished once by the initial author (the original PR). This is why it is really ideal to look at things mentioned for review and look closely, as others are likely going to maintain it for you, and this is a perpetual task. Since it is, going the same way as other code lightens the load.
return; | ||
} | ||
jdbi.getConfig(SqlStatements.class) | ||
.addContextListener(new StatementContextListener() { |
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.
one last thing is that this code would be easier to read and more conventional extracted to a package-private type (TracingStatementContextListener) and then used here. Another way to reduce ceremony is to switch the whole type to TracingStatementContextListener similar to this https://github.com/openzipkin/brave/blob/master/instrumentation/okhttp3/src/main/java/brave/okhttp3/TracingCallFactory.java (keeping the builder if you decide to keep the toggle).
This allows people to configure JDBI however they want including via a plugin and keeps the complexity of this code to a minimal. In other words, the actual code is the context listener, plugin is just one way to configure it. By limiting to plugin we add more harder to test code here, to save someone from doing jdbi.getConfig(SqlStatements.class) .addContextListener(our_listener)
despite that being how all the other instrumentation work (direct integration where possible)
Making this comment as on the mind, but like I said you can feel free to stop refactoring if you have run out of time. If I were being strict like years ago I would have insisted on it.
f5fd953
to
65f563f
Compare
instrumentation/jdbi3/src/main/java/brave/jdbi3/BraveStatementContextListener.java
Outdated
Show resolved
Hide resolved
65f563f
to
2ecc9d4
Compare
instrumentation/jdbi3/src/main/java/brave/jdbi3/BraveStatementContextListener.java
Outdated
Show resolved
Hide resolved
instrumentation/jdbi3/src/main/java/brave/jdbi3/BraveStatementContextListener.java
Outdated
Show resolved
Hide resolved
instrumentation/jdbi3/src/main/java/brave/jdbi3/BraveStatementContextListener.java
Outdated
Show resolved
Hide resolved
2ecc9d4
to
f803bb1
Compare
instrumentation/jdbi3/src/test/java/brave/jdbi3/BraveStatementContextListenerTest.java
Outdated
Show resolved
Hide resolved
f803bb1
to
9f9842b
Compare
9f9842b
to
cb6d68f
Compare
import java.util.regex.Matcher; | ||
import java.util.regex.Pattern; | ||
|
||
public class RemoteServiceNameResolver { |
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.
Super minor, I think we shouldn't expose this class publicly:
public class RemoteServiceNameResolver { | |
final class RemoteServiceNameResolver { |
}); | ||
} | ||
|
||
String getRemoteServiceName(StatementContext ctx) { |
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.
👍 thank you
@codefromthecrypt mind taking another look? thank you! |
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.
sure I'll do a small amount of polishing post merge. good work!
This converts jdbi3 instrumentation to be more like MongoDB in implementation: configured by instance, not static like mysql connection driver. It also pares back the data policy to be the same as MySQL: this allows us a base case to move forward with. Finally, it switches implementation to a StatementLogger, so that exceptions are visible. Infrastructure wise, this re-uses the more recent style from instrumentation here, including docker for the IT. This is a follow-up to #1460 before we release a version, so API breaks are not an issue, yet. Thanks to @reftel for the initial work on this, and @reta on the review of that. I did my best in RATIONALE.md here to explain why the changes are important. --------- Signed-off-by: Adrian Cole <[email protected]>
Adds a plugin for JDBI 3 (https://jdbi.org/ ) that sends spans via Brave for all SQL commands run. Tag names are chosen to match the old P6Spy instrumentation from the 5.x series.
Including both a Mocktio test and an integration test that uses H2. Not sure both are needed, as they test pretty much the same thing.