diff --git a/instrumentation/jdbc-mariadb-3.0.0/build.gradle b/instrumentation/jdbc-mariadb-3.0.0/build.gradle new file mode 100644 index 0000000000..231e97352d --- /dev/null +++ b/instrumentation/jdbc-mariadb-3.0.0/build.gradle @@ -0,0 +1,22 @@ +dependencies { + implementation(project(":agent-bridge")) + implementation(project(":agent-bridge-datastore")) + implementation("org.mariadb.jdbc:mariadb-java-client:3.0.3") + + testImplementation("ch.vorburger.mariaDB4j:mariaDB4j:2.2.1") + testImplementation(project(":instrumentation:jdbc-generic")){ transitive = false } +} + +jar { + manifest { attributes 'Implementation-Title': 'com.newrelic.instrumentation.jdbc-mariadb-3.0.0' } +} + +verifyInstrumentation { + passesOnly 'org.mariadb.jdbc:mariadb-java-client:(3.0.2-rc,)' + exclude 'org.mariadb.jdbc:mariadb-java-client:[3.0.0-alpha,3.0.2-rc]' +} + +site { + title 'MariaDB Java Client' + type 'Datastore' +} \ No newline at end of file diff --git a/instrumentation/jdbc-mariadb-3.0.0/src/main/java/com/nr/agent/instrumentation/jdbc/mariadb/MariaDbDatabaseVendor.java b/instrumentation/jdbc-mariadb-3.0.0/src/main/java/com/nr/agent/instrumentation/jdbc/mariadb/MariaDbDatabaseVendor.java new file mode 100644 index 0000000000..9c099df1c1 --- /dev/null +++ b/instrumentation/jdbc-mariadb-3.0.0/src/main/java/com/nr/agent/instrumentation/jdbc/mariadb/MariaDbDatabaseVendor.java @@ -0,0 +1,27 @@ +/* + * + * * Copyright 2020 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package com.nr.agent.instrumentation.jdbc.mariadb; + +import com.newrelic.agent.bridge.datastore.DatabaseVendor; +import com.newrelic.agent.bridge.datastore.DatastoreVendor; +import com.newrelic.agent.bridge.datastore.JdbcDatabaseVendor; + +public class MariaDbDatabaseVendor extends JdbcDatabaseVendor { + + public static final DatabaseVendor INSTANCE = new MariaDbDatabaseVendor(); + + private MariaDbDatabaseVendor() { + super("MariaDB", "mysql", false); + } + + @Override + public DatastoreVendor getDatastoreVendor() { + return DatastoreVendor.MySQL; + } + +} \ No newline at end of file diff --git a/instrumentation/jdbc-mariadb-3.0.0/src/main/java/org/mariadb/jdbc/Driver.java b/instrumentation/jdbc-mariadb-3.0.0/src/main/java/org/mariadb/jdbc/Driver.java new file mode 100644 index 0000000000..17dde4af73 --- /dev/null +++ b/instrumentation/jdbc-mariadb-3.0.0/src/main/java/org/mariadb/jdbc/Driver.java @@ -0,0 +1,25 @@ +/* + * + * * Copyright 2020 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package org.mariadb.jdbc; + +import java.util.Properties; + +import com.newrelic.agent.bridge.datastore.JdbcHelper; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import com.nr.agent.instrumentation.jdbc.mariadb.MariaDbDatabaseVendor; + +@Weave +public abstract class Driver { + + public Connection connect(String url, Properties props) { + JdbcHelper.putVendor(getClass(), MariaDbDatabaseVendor.INSTANCE); + return Weaver.callOriginal(); + } + +} diff --git a/instrumentation/jdbc-mariadb-3.0.0/src/main/java/org/mariadb/jdbc/MariaDbDataSource.java b/instrumentation/jdbc-mariadb-3.0.0/src/main/java/org/mariadb/jdbc/MariaDbDataSource.java new file mode 100644 index 0000000000..b743666525 --- /dev/null +++ b/instrumentation/jdbc-mariadb-3.0.0/src/main/java/org/mariadb/jdbc/MariaDbDataSource.java @@ -0,0 +1,30 @@ +/* + * + * * Copyright 2020 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package org.mariadb.jdbc; + +import java.sql.Connection; + +import com.newrelic.agent.bridge.datastore.JdbcHelper; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import com.nr.agent.instrumentation.jdbc.mariadb.MariaDbDatabaseVendor; + +@Weave +public abstract class MariaDbDataSource { + + public Connection getConnection() { + JdbcHelper.putVendor(getClass(), MariaDbDatabaseVendor.INSTANCE); + return Weaver.callOriginal(); + } + + public Connection getConnection(String user, String password) { + JdbcHelper.putVendor(getClass(), MariaDbDatabaseVendor.INSTANCE); + return Weaver.callOriginal(); + } + +} \ No newline at end of file diff --git a/instrumentation/jdbc-mariadb-3.0.0/src/test/java/org/mariadb/jdbc/MariaDbTest.java b/instrumentation/jdbc-mariadb-3.0.0/src/test/java/org/mariadb/jdbc/MariaDbTest.java new file mode 100644 index 0000000000..4c34e7deeb --- /dev/null +++ b/instrumentation/jdbc-mariadb-3.0.0/src/test/java/org/mariadb/jdbc/MariaDbTest.java @@ -0,0 +1,177 @@ +/* + * + * * Copyright 2020 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package org.mariadb.jdbc; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.Statement; + +import ch.vorburger.mariadb4j.DB; +import ch.vorburger.mariadb4j.DBConfigurationBuilder; +import com.newrelic.agent.introspec.DatastoreHelper; +import com.newrelic.agent.introspec.InstrumentationTestConfig; +import com.newrelic.agent.introspec.InstrumentationTestRunner; +import com.newrelic.agent.introspec.Introspector; +import com.newrelic.api.agent.Trace; +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Ignore; +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(InstrumentationTestRunner.class) +@InstrumentationTestConfig(includePrefixes = {"org.mariadb.jdbc"}) +public class MariaDbTest { + + private static DB mariaDb; + + private static String connectionString; + private static String dbName; + + @BeforeClass + public static void setUpDb() throws Exception { + DBConfigurationBuilder builder = DBConfigurationBuilder.newBuilder() + .setPort(0); // This will automatically find a free port + + dbName = "MariaDB" + System.currentTimeMillis(); + mariaDb = DB.newEmbeddedDB(builder.build()); + connectionString = builder.getURL(dbName); + mariaDb.start(); + + mariaDb.createDB(dbName); + mariaDb.source("maria-db-test.sql", null, null, dbName); + } + + @AfterClass + public static void tearDownDb() throws Exception { + mariaDb.stop(); + } + + @Test + @Ignore + public void testPreparedStatementQuery() throws Exception { + mariaDbPreparedStatementQuery(); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + Assert.assertEquals(1, introspector.getFinishedTransactionCount()); + DatastoreHelper helper = new DatastoreHelper("MySQL"); + helper.assertAggregateMetrics(); + helper.assertUnscopedOperationMetricCount("select", 1); + } + + @Test + @Ignore + public void testCrud() throws Exception { + mariaDbInsert(); + mariaDbReadInsert(); + mariaDbUpdate(); + mariaDbReadUpdate(); + mariaDbDelete(); + mariaDbReadDelete(); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + Assert.assertEquals(6, introspector.getFinishedTransactionCount()); + + DatastoreHelper helper = new DatastoreHelper("MySQL"); + helper.assertAggregateMetrics(); + helper.assertUnscopedOperationMetricCount("insert", 1); // C + helper.assertUnscopedOperationMetricCount("select", 3); // R (once per step) + helper.assertUnscopedOperationMetricCount("update", 1); // U + helper.assertUnscopedOperationMetricCount("delete", 1); // D + } + + @Trace(dispatcher = true) + public void mariaDbPreparedStatementQuery() throws Exception { + Connection connection = DriverManager.getConnection(connectionString, "root", ""); + PreparedStatement statement = connection.prepareStatement("SELECT id FROM testQuery WHERE value LIKE ?"); + statement.setString(1, "cool"); + ResultSet resultSet = statement.executeQuery(); + if (resultSet.next()) { + long value = resultSet.getLong(1); + Assert.assertEquals(1, value); + } else { + Assert.fail("Unable to get any results from database"); + } + connection.close(); + } + + @Trace(dispatcher = true) + public void mariaDbInsert() throws Exception { + Connection connection = DriverManager.getConnection(connectionString, "root", ""); + + PreparedStatement statement = connection.prepareStatement("INSERT INTO testCrud (id, value) VALUES (1, ?)"); + statement.setString(1, "insert"); + int inserted = statement.executeUpdate(); + Assert.assertEquals(1, inserted); // Only 1 row to insert + connection.close(); + } + + + @Trace(dispatcher = true) + public void mariaDbReadInsert() throws Exception { + Connection connection = DriverManager.getConnection(connectionString, "root", ""); + Statement statement = connection.createStatement(); + ResultSet resultSet = statement.executeQuery("SELECT id FROM testCrud WHERE value = 'insert'"); + if (resultSet.next()) { + long value = resultSet.getLong(1); + Assert.assertEquals(1, value); + } else { + Assert.fail("Unable to find inserted row"); + } + connection.close(); + } + + @Trace(dispatcher = true) + public void mariaDbUpdate() throws Exception { + Connection connection = DriverManager.getConnection(connectionString, "root", ""); + + PreparedStatement statement = connection.prepareStatement("UPDATE testCrud SET value = ? WHERE id = ?"); + statement.setString(1, "update"); + statement.setInt(2, 1); + int updated = statement.executeUpdate(); + Assert.assertEquals(1, updated); // Only 1 row to update + connection.close(); + } + + @Trace(dispatcher = true) + public void mariaDbReadUpdate() throws Exception { + Connection connection = DriverManager.getConnection(connectionString, "root", ""); + Statement statement = connection.createStatement(); + statement.execute("SELECT value FROM testCrud WHERE id = 1"); + ResultSet resultSet = statement.getResultSet(); + if (resultSet.next()) { + String value = resultSet.getString(1); + Assert.assertEquals("update", value); + } else { + Assert.fail("Unable to find updated row"); + } + connection.close(); + } + + @Trace(dispatcher = true) + public void mariaDbDelete() throws Exception { + Connection connection = DriverManager.getConnection(connectionString, "root", ""); + + Statement statement = connection.createStatement(); + int updated = statement.executeUpdate("DELETE FROM testCrud WHERE id = 1"); + Assert.assertEquals(1, updated); // Only 1 row to remove + connection.close(); + } + + @Trace(dispatcher = true) + public void mariaDbReadDelete() throws Exception { + Connection connection = DriverManager.getConnection(connectionString, "root", ""); + PreparedStatement statement = connection.prepareStatement("SELECT * FROM testCrud"); + ResultSet resultSet = statement.executeQuery(); + Assert.assertFalse("We found a row when we didn't expect one", resultSet.next()); + connection.close(); + } +} diff --git a/instrumentation/jdbc-mariadb-3.0.0/src/test/resources/maria-db-test.sql b/instrumentation/jdbc-mariadb-3.0.0/src/test/resources/maria-db-test.sql new file mode 100644 index 0000000000..ea1011be2a --- /dev/null +++ b/instrumentation/jdbc-mariadb-3.0.0/src/test/resources/maria-db-test.sql @@ -0,0 +1,6 @@ +CREATE TABLE testQuery (id int not null primary key, value varchar(20)); +INSERT INTO testQuery (id, value) VALUES (1, 'cool'); +INSERT INTO testQuery (id, value) VALUES (2, 'nice'); +INSERT INTO testQuery (id, value) VALUES (3, 'sweet'); + +CREATE TABLE testCrud (id int not null primary key, value varchar(20)); \ No newline at end of file diff --git a/instrumentation/r2dbc-mariadb-1.1.2/build.gradle b/instrumentation/r2dbc-mariadb-1.1.2/build.gradle new file mode 100644 index 0000000000..2f6d68fe02 --- /dev/null +++ b/instrumentation/r2dbc-mariadb-1.1.2/build.gradle @@ -0,0 +1,20 @@ +dependencies { + implementation(project(":agent-bridge")) + implementation(project(":agent-bridge-datastore")) + implementation("org.mariadb:r2dbc-mariadb:1.1.2") + testImplementation("ch.vorburger.mariaDB4j:mariaDB4j:2.2.1") +} + +jar { + manifest { attributes 'Implementation-Title': 'com.newrelic.instrumentation.r2dbc-mariadb-1.1.2' } +} + +verifyInstrumentation { + passesOnly 'org.mariadb:r2dbc-mariadb:[1.1.2,1.2.1)' + excludeRegex(".*(alpha|beta|rc).*") +} + +site { + title 'MariaDB R2DBC' + type 'Datastore' +} diff --git a/instrumentation/r2dbc-mariadb-1.1.2/src/main/java/com/nr/agent/instrumentation/r2dbc/CancelHandler.java b/instrumentation/r2dbc-mariadb-1.1.2/src/main/java/com/nr/agent/instrumentation/r2dbc/CancelHandler.java new file mode 100644 index 0000000000..7b54b687f0 --- /dev/null +++ b/instrumentation/r2dbc-mariadb-1.1.2/src/main/java/com/nr/agent/instrumentation/r2dbc/CancelHandler.java @@ -0,0 +1,20 @@ +package com.nr.agent.instrumentation.r2dbc; + + +public class CancelHandler implements Runnable { + + private NRHolder holder = null; + + public CancelHandler(NRHolder hold) { + holder = hold; + } + + + @Override + public void run() { + if(holder != null) { + holder.ignore(); + } + } + +} diff --git a/instrumentation/r2dbc-mariadb-1.1.2/src/main/java/com/nr/agent/instrumentation/r2dbc/NRHolder.java b/instrumentation/r2dbc-mariadb-1.1.2/src/main/java/com/nr/agent/instrumentation/r2dbc/NRHolder.java new file mode 100644 index 0000000000..981c7dbec5 --- /dev/null +++ b/instrumentation/r2dbc-mariadb-1.1.2/src/main/java/com/nr/agent/instrumentation/r2dbc/NRHolder.java @@ -0,0 +1,46 @@ +package com.nr.agent.instrumentation.r2dbc; + + +import java.util.function.Consumer; + +import com.newrelic.api.agent.DatastoreParameters; +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.Segment; + +public class NRHolder implements Consumer, Runnable { + + private Segment segment = null; + private DatastoreParameters params = null; + + public NRHolder(Segment seg, DatastoreParameters p) { + segment = seg; + params = p; + } + + @Override + public void accept(Throwable t) { + NewRelic.noticeError(t); + segment.ignore(); + segment = null; + + } + + @Override + public void run() { + if(segment != null) { + if(params != null) { + segment.reportAsExternal(params); + } + segment.end(); + segment = null; + } + } + + public void ignore() { + if(segment != null) { + segment.ignore(); + segment = null; + } + } + +} diff --git a/instrumentation/r2dbc-mariadb-1.1.2/src/main/java/com/nr/agent/instrumentation/r2dbc/R2dbcUtils.java b/instrumentation/r2dbc-mariadb-1.1.2/src/main/java/com/nr/agent/instrumentation/r2dbc/R2dbcUtils.java new file mode 100644 index 0000000000..e4257e5747 --- /dev/null +++ b/instrumentation/r2dbc-mariadb-1.1.2/src/main/java/com/nr/agent/instrumentation/r2dbc/R2dbcUtils.java @@ -0,0 +1,61 @@ +package com.nr.agent.instrumentation.r2dbc; + +import com.newrelic.agent.bridge.NoOpTransaction; +import com.newrelic.agent.bridge.datastore.OperationAndTableName; +import com.newrelic.agent.bridge.datastore.R2dbcObfuscator; +import com.newrelic.agent.bridge.datastore.R2dbcOperation; +import com.newrelic.api.agent.DatastoreParameters; +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.Segment; +import com.newrelic.api.agent.Transaction; +import org.mariadb.r2dbc.api.MariadbResult; +import org.mariadb.r2dbc.client.Client; +import org.reactivestreams.Subscription; +import reactor.core.publisher.Flux; + +import java.util.function.Consumer; + +public class R2dbcUtils { + public static Flux wrapRequest(Flux request, String sql, Client client) { + if(request != null) { + Transaction transaction = NewRelic.getAgent().getTransaction(); + if(transaction != null && !(transaction instanceof NoOpTransaction)) { + Segment segment = transaction.startSegment("execute"); + return request + .doOnSubscribe(reportExecution(sql, client, segment)) + .doFinally((type) -> segment.end()); + } + } + return request; + } + + private static Consumer reportExecution(String sql, Client client, Segment segment) { + return (subscription) -> { + OperationAndTableName sqlOperation = R2dbcOperation.extractFrom(sql); + if (sqlOperation != null) { + segment.reportAsExternal(DatastoreParameters + .product("MariaDB") + .collection(sqlOperation.getTableName()) + .operation(sqlOperation.getOperation()) + .instance(client.getHostAddress().getHost(), client.getHostAddress().getPort()) + .databaseName(client.getContext().getDatabase()) + .slowQuery(sql, R2dbcObfuscator.MYSQL_QUERY_CONVERTER) + .build()); + } + }; + } + + public static DatastoreParameters getParams(String sql, Client client) { + OperationAndTableName sqlOperation = R2dbcOperation.extractFrom(sql); + if(sqlOperation == null) return null; + return DatastoreParameters + .product("MariaDB") + .collection(sqlOperation.getTableName()) + .operation(sqlOperation.getOperation()) + .instance(client.getHostAddress().getHost(), client.getHostAddress().getPort()) + .databaseName(client.getContext().getDatabase()) + .slowQuery(sql, R2dbcObfuscator.MYSQL_QUERY_CONVERTER) + .build(); + } + +} diff --git a/instrumentation/r2dbc-mariadb-1.1.2/src/main/java/org/mariadb/r2dbc/MariadbClientParameterizedQueryStatement_Instrumentation.java b/instrumentation/r2dbc-mariadb-1.1.2/src/main/java/org/mariadb/r2dbc/MariadbClientParameterizedQueryStatement_Instrumentation.java new file mode 100644 index 0000000000..9a41e1af24 --- /dev/null +++ b/instrumentation/r2dbc-mariadb-1.1.2/src/main/java/org/mariadb/r2dbc/MariadbClientParameterizedQueryStatement_Instrumentation.java @@ -0,0 +1,36 @@ +package org.mariadb.r2dbc; + +import org.mariadb.r2dbc.api.MariadbResult; +import org.mariadb.r2dbc.client.Client; +import org.mariadb.r2dbc.message.Protocol; + +import com.newrelic.api.agent.DatastoreParameters; +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.Segment; +import com.newrelic.api.agent.Trace; +import com.newrelic.api.agent.weaver.MatchType; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import com.nr.agent.instrumentation.r2dbc.CancelHandler; +import com.nr.agent.instrumentation.r2dbc.NRHolder; +import com.nr.agent.instrumentation.r2dbc.R2dbcUtils; + +import reactor.core.publisher.Flux; + +@Weave(type = MatchType.ExactClass, originalName = "org.mariadb.r2dbc.MariadbClientParameterizedQueryStatement") +abstract class MariadbClientParameterizedQueryStatement_Instrumentation extends MariadbCommonStatement { + + MariadbClientParameterizedQueryStatement_Instrumentation( + Client client, String sql, MariadbConnectionConfiguration configuration) { + super(client, sql, configuration, Protocol.TEXT); + } + + @Trace(dispatcher = true) + public Flux execute() { + Flux request = Weaver.callOriginal(); + DatastoreParameters params = R2dbcUtils.getParams(initialSql, client); + Segment segment = NewRelic.getAgent().getTransaction().startSegment("execute"); + NRHolder holder = new NRHolder(segment, params); + return request.doOnError(holder).doOnTerminate(holder).doOnCancel(new CancelHandler(holder)); + } +} diff --git a/instrumentation/r2dbc-mariadb-1.1.2/src/main/java/org/mariadb/r2dbc/MariadbServerParameterizedQueryStatement_Instrumentation.java b/instrumentation/r2dbc-mariadb-1.1.2/src/main/java/org/mariadb/r2dbc/MariadbServerParameterizedQueryStatement_Instrumentation.java new file mode 100644 index 0000000000..72485cfe32 --- /dev/null +++ b/instrumentation/r2dbc-mariadb-1.1.2/src/main/java/org/mariadb/r2dbc/MariadbServerParameterizedQueryStatement_Instrumentation.java @@ -0,0 +1,34 @@ +package org.mariadb.r2dbc; + +import org.mariadb.r2dbc.api.MariadbResult; +import org.mariadb.r2dbc.client.Client; +import org.mariadb.r2dbc.message.Protocol; + +import com.newrelic.api.agent.DatastoreParameters; +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.Segment; +import com.newrelic.api.agent.weaver.MatchType; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import com.nr.agent.instrumentation.r2dbc.CancelHandler; +import com.nr.agent.instrumentation.r2dbc.NRHolder; +import com.nr.agent.instrumentation.r2dbc.R2dbcUtils; + +import reactor.core.publisher.Flux; + +@Weave(type = MatchType.ExactClass, originalName = "org.mariadb.r2dbc.MariadbServerParameterizedQueryStatement") +abstract class MariadbServerParameterizedQueryStatement_Instrumentation extends MariadbCommonStatement { + + MariadbServerParameterizedQueryStatement_Instrumentation( + Client client, String sql, MariadbConnectionConfiguration configuration) { + super(client, sql, configuration, Protocol.BINARY); + } + + public Flux execute() { + Flux request = Weaver.callOriginal(); + DatastoreParameters params = R2dbcUtils.getParams(initialSql, client); + Segment segment = NewRelic.getAgent().getTransaction().startSegment("execute"); + NRHolder holder = new NRHolder(segment, params); + return request.doOnError(holder).doOnTerminate(holder).doOnCancel(new CancelHandler(holder)); + } +} diff --git a/instrumentation/r2dbc-mariadb-1.1.2/src/test/java/com/nr/agent/instrumentation/r2dbc/MariadbInstrumentedTest.java b/instrumentation/r2dbc-mariadb-1.1.2/src/test/java/com/nr/agent/instrumentation/r2dbc/MariadbInstrumentedTest.java new file mode 100644 index 0000000000..6d8652a318 --- /dev/null +++ b/instrumentation/r2dbc-mariadb-1.1.2/src/test/java/com/nr/agent/instrumentation/r2dbc/MariadbInstrumentedTest.java @@ -0,0 +1,71 @@ +package com.nr.agent.instrumentation.r2dbc; + +import ch.vorburger.mariadb4j.DB; +import ch.vorburger.mariadb4j.DBConfigurationBuilder; +import com.newrelic.agent.introspec.DatastoreHelper; +import com.newrelic.agent.introspec.InstrumentationTestConfig; +import com.newrelic.agent.introspec.InstrumentationTestRunner; +import com.newrelic.agent.introspec.Introspector; +import io.r2dbc.spi.Connection; +import io.r2dbc.spi.ConnectionFactories; +import io.r2dbc.spi.ConnectionFactory; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import reactor.core.publisher.Mono; + +import static org.junit.Assert.assertEquals; + +@RunWith(InstrumentationTestRunner.class) +@InstrumentationTestConfig(includePrefixes = "org.mariadb.r2dbc") +public class MariadbInstrumentedTest { + + public static DB mariaDb; + public Connection connection; + + @Before + public void setup() throws Exception { + String databaseName = "MariaDB" + System.currentTimeMillis(); + DBConfigurationBuilder builder = DBConfigurationBuilder.newBuilder().setPort(0); + mariaDb = DB.newEmbeddedDB(builder.build()); + mariaDb.start(); + mariaDb.createDB(databaseName); + mariaDb.source("users.sql", "user", "password", databaseName); + ConnectionFactory connectionFactory = ConnectionFactories.get(builder.getURL(databaseName).replace("mysql", "mariadb").replace("jdbc", "r2dbc").replace("localhost", "user:password@localhost")); + connection = Mono.from(connectionFactory.create()).block(); + } + + @AfterClass + public static void teardown() throws Exception { + mariaDb.stop(); + } + + @Test + public void testSelect() { + //Given + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + DatastoreHelper helper = new DatastoreHelper("MariaDB"); + + //When + R2dbcTestUtils.basicRequests(connection); + + //Then + assertEquals(1, introspector.getFinishedTransactionCount(1000)); + assertEquals(1, introspector.getTransactionNames().size()); + String transactionName = introspector.getTransactionNames().stream().findFirst().orElse(""); + helper.assertScopedStatementMetricCount(transactionName, "INSERT", "USERS", 1); + helper.assertScopedStatementMetricCount(transactionName, "SELECT", "USERS", 3); + helper.assertScopedStatementMetricCount(transactionName, "UPDATE", "USERS", 1); + helper.assertScopedStatementMetricCount(transactionName, "DELETE", "USERS", 1); + helper.assertAggregateMetrics(); + helper.assertUnscopedOperationMetricCount("INSERT", 1); + helper.assertUnscopedOperationMetricCount("SELECT", 3); + helper.assertUnscopedOperationMetricCount("UPDATE", 1); + helper.assertUnscopedOperationMetricCount("DELETE", 1); + helper.assertUnscopedStatementMetricCount("INSERT", "USERS", 1); + helper.assertUnscopedStatementMetricCount("SELECT", "USERS", 3); + helper.assertUnscopedStatementMetricCount("UPDATE", "USERS", 1); + helper.assertUnscopedStatementMetricCount("DELETE", "USERS", 1); + } +} \ No newline at end of file diff --git a/instrumentation/r2dbc-mariadb-1.1.2/src/test/java/com/nr/agent/instrumentation/r2dbc/MariadbNoInstrumentationTest.java b/instrumentation/r2dbc-mariadb-1.1.2/src/test/java/com/nr/agent/instrumentation/r2dbc/MariadbNoInstrumentationTest.java new file mode 100644 index 0000000000..d53d163f07 --- /dev/null +++ b/instrumentation/r2dbc-mariadb-1.1.2/src/test/java/com/nr/agent/instrumentation/r2dbc/MariadbNoInstrumentationTest.java @@ -0,0 +1,62 @@ +package com.nr.agent.instrumentation.r2dbc; + +import ch.vorburger.mariadb4j.DB; +import ch.vorburger.mariadb4j.DBConfigurationBuilder; +import com.newrelic.agent.introspec.DatastoreHelper; +import com.newrelic.agent.introspec.InstrumentationTestConfig; +import com.newrelic.agent.introspec.InstrumentationTestRunner; +import com.newrelic.agent.introspec.Introspector; +import io.r2dbc.spi.Connection; +import io.r2dbc.spi.ConnectionFactories; +import io.r2dbc.spi.ConnectionFactory; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import reactor.core.publisher.Mono; + +import static org.junit.Assert.assertEquals; + +@RunWith(InstrumentationTestRunner.class) +@InstrumentationTestConfig(includePrefixes = "none") +public class MariadbNoInstrumentationTest { + + public static DB mariaDb; + public Connection connection; + + @Before + public void setup() throws Exception { + String databaseName = "MariaDB" + System.currentTimeMillis(); + DBConfigurationBuilder builder = DBConfigurationBuilder.newBuilder().setPort(0); + mariaDb = DB.newEmbeddedDB(builder.build()); + mariaDb.start(); + mariaDb.createDB(databaseName); + mariaDb.source("users.sql", "user", "password", databaseName); + ConnectionFactory connectionFactory = ConnectionFactories.get(builder.getURL(databaseName).replace("mysql", "mariadb").replace("jdbc", "r2dbc").replace("localhost", "user:password@localhost")); + connection = Mono.from(connectionFactory.create()).block(); + } + + @AfterClass + public static void teardown() throws Exception { + mariaDb.stop(); + } + + @Test + public void testSelect() { + //Given + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + DatastoreHelper helper = new DatastoreHelper("MariaDB"); + + //When + R2dbcTestUtils.basicRequests(connection); + + //Then + assertEquals(1, introspector.getFinishedTransactionCount(1000)); + assertEquals(1, introspector.getTransactionNames().size()); + String transactionName = introspector.getTransactionNames().stream().findFirst().orElse(""); + helper.assertScopedStatementMetricCount(transactionName, "INSERT", "USERS", 0); + helper.assertScopedStatementMetricCount(transactionName, "SELECT", "USERS", 0); + helper.assertScopedStatementMetricCount(transactionName, "UPDATE", "USERS", 0); + helper.assertScopedStatementMetricCount(transactionName, "DELETE", "USERS", 0); + } +} \ No newline at end of file diff --git a/instrumentation/r2dbc-mariadb-1.1.2/src/test/java/com/nr/agent/instrumentation/r2dbc/R2dbcTestUtils.java b/instrumentation/r2dbc-mariadb-1.1.2/src/test/java/com/nr/agent/instrumentation/r2dbc/R2dbcTestUtils.java new file mode 100644 index 0000000000..885f3d9d9a --- /dev/null +++ b/instrumentation/r2dbc-mariadb-1.1.2/src/test/java/com/nr/agent/instrumentation/r2dbc/R2dbcTestUtils.java @@ -0,0 +1,91 @@ +package com.nr.agent.instrumentation.r2dbc; + +import java.util.List; + +import org.reactivestreams.Publisher; + +import com.newrelic.api.agent.Trace; + +import io.r2dbc.spi.Connection; +import io.r2dbc.spi.Result; +import io.r2dbc.spi.Statement; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +public class R2dbcTestUtils { + @Trace(dispatcher = true) + public static void basicRequests(Connection connection) { +// Mono.from(connection.createStatement("INSERT INTO USERS(id, first_name, last_name, age) VALUES(1, 'Max', 'Power', 30)").execute()).block(); +// Mono.from(connection.createStatement("SELECT * FROM USERS WHERE last_name='Power'").execute()).block(); +// Mono.from(connection.createStatement("UPDATE USERS SET age = 36 WHERE last_name = 'Power'").execute()).block(); +// Mono.from(connection.createStatement("SELECT * FROM USERS WHERE last_name='Power'").execute()).block(); +// Mono.from(connection.createStatement("DELETE FROM USERS WHERE last_name = 'Power'").execute()).block(); +// Mono.from(connection.createStatement("SELECT * FROM USERS").execute()).block(); + Statement stmt = connection.createStatement("INSERT INTO USERS(id, first_name, last_name, age) VALUES(1, 'Max', 'Power', 30)"); + Publisher publish = stmt.execute(); + Integer i = Flux.from(publish).flatMap(it -> it.getRowsUpdated()).blockLast(); + System.out.println("Inserted " + i + " row(s)"); + publish = connection.createStatement("SELECT * FROM USERS WHERE last_name='Power'").execute(); + Mono> users = Flux.from(publish).flatMap(it -> it.map((row,rowMetadata) -> + new User(row.get("first_name", String.class),row.get("last_name", String.class),row.get("age", Integer.class)))).collectList(); + List userList = users.block(); + System.out.println("Select returned " + userList.size() + " users"); + int count = 1; + for(User user : userList) { + + System.out.println("user " + count + ": " + user); + count++; + } + + publish = connection.createStatement("UPDATE USERS SET age = 36 WHERE last_name = 'Power'").execute(); + i = Flux.from(publish).flatMap(it -> it.getRowsUpdated()).blockLast(); + System.out.println("Updated ages of " + i + " users with last name Power"); + + publish = connection.createStatement("SELECT * FROM USERS WHERE last_name='Power'").execute(); + users = Flux.from(publish).flatMap(it -> it.map((row,rowMetadata) -> + new User(row.get("first_name", String.class),row.get("last_name", String.class),row.get("age", Integer.class)))).collectList(); + userList = users.block(); + System.out.println("Select returned " + userList.size() + " users"); + count = 1; + for(User user : userList) { + + System.out.println("user " + count + ": " + user); + count++; + } + + + + publish = connection.createStatement("DELETE FROM USERS WHERE last_name = 'Power'").execute(); + i = Flux.from(publish).flatMap(it -> it.getRowsUpdated()).blockLast(); + System.out.println("Deleted " + i + " users with last name Power"); + + publish = connection.createStatement("SELECT * FROM USERS WHERE last_name='Power'").execute(); + users = Flux.from(publish).flatMap(it -> it.map((row,rowMetadata) -> + new User(row.get("first_name", String.class),row.get("last_name", String.class),row.get("age", Integer.class)))).collectList(); + userList = users.block(); + System.out.println("Select returned " + userList.size() + " users"); + count = 1; + for(User user : userList) { + + System.out.println("user " + count + ": " + user); + count++; + } + } + + public static class User { + String first; + String last; + int age; + + public User(String fn, String ln, int a) { + first = fn; + last = ln; + age = a; + } + + public String toString() { + return "Name: " + first + " " + last + ", age: " + age; + } + } + +} diff --git a/instrumentation/r2dbc-mariadb-1.1.2/src/test/resources/users.sql b/instrumentation/r2dbc-mariadb-1.1.2/src/test/resources/users.sql new file mode 100644 index 0000000000..6380228532 --- /dev/null +++ b/instrumentation/r2dbc-mariadb-1.1.2/src/test/resources/users.sql @@ -0,0 +1,2 @@ +CREATE TABLE IF NOT EXISTS USERS(id int primary key, first_name varchar(255), last_name varchar(255), age int); +TRUNCATE TABLE USERS \ No newline at end of file diff --git a/instrumentation/r2dbc-mariadb-1.2.1/build.gradle b/instrumentation/r2dbc-mariadb-1.2.1/build.gradle new file mode 100644 index 0000000000..a185aa1721 --- /dev/null +++ b/instrumentation/r2dbc-mariadb-1.2.1/build.gradle @@ -0,0 +1,20 @@ +dependencies { + implementation(project(":agent-bridge")) + implementation(project(":agent-bridge-datastore")) + implementation("org.mariadb:r2dbc-mariadb:1.2.1") + testImplementation("ch.vorburger.mariaDB4j:mariaDB4j:2.2.1") +} + +jar { + manifest { attributes 'Implementation-Title': 'com.newrelic.instrumentation.r2dbc-mariadb-1.2.1' } +} + +verifyInstrumentation { + passes 'org.mariadb:r2dbc-mariadb:[1.2.1,)' + excludeRegex(".*(alpha|beta|rc).*") +} + +site { + title 'MariaDB R2DBC' + type 'Datastore' +} diff --git a/instrumentation/r2dbc-mariadb-1.2.1/src/main/java/com/nr/agent/instrumentation/r2dbc/CancelHandler.java b/instrumentation/r2dbc-mariadb-1.2.1/src/main/java/com/nr/agent/instrumentation/r2dbc/CancelHandler.java new file mode 100644 index 0000000000..7b54b687f0 --- /dev/null +++ b/instrumentation/r2dbc-mariadb-1.2.1/src/main/java/com/nr/agent/instrumentation/r2dbc/CancelHandler.java @@ -0,0 +1,20 @@ +package com.nr.agent.instrumentation.r2dbc; + + +public class CancelHandler implements Runnable { + + private NRHolder holder = null; + + public CancelHandler(NRHolder hold) { + holder = hold; + } + + + @Override + public void run() { + if(holder != null) { + holder.ignore(); + } + } + +} diff --git a/instrumentation/r2dbc-mariadb-1.2.1/src/main/java/com/nr/agent/instrumentation/r2dbc/NRHolder.java b/instrumentation/r2dbc-mariadb-1.2.1/src/main/java/com/nr/agent/instrumentation/r2dbc/NRHolder.java new file mode 100644 index 0000000000..981c7dbec5 --- /dev/null +++ b/instrumentation/r2dbc-mariadb-1.2.1/src/main/java/com/nr/agent/instrumentation/r2dbc/NRHolder.java @@ -0,0 +1,46 @@ +package com.nr.agent.instrumentation.r2dbc; + + +import java.util.function.Consumer; + +import com.newrelic.api.agent.DatastoreParameters; +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.Segment; + +public class NRHolder implements Consumer, Runnable { + + private Segment segment = null; + private DatastoreParameters params = null; + + public NRHolder(Segment seg, DatastoreParameters p) { + segment = seg; + params = p; + } + + @Override + public void accept(Throwable t) { + NewRelic.noticeError(t); + segment.ignore(); + segment = null; + + } + + @Override + public void run() { + if(segment != null) { + if(params != null) { + segment.reportAsExternal(params); + } + segment.end(); + segment = null; + } + } + + public void ignore() { + if(segment != null) { + segment.ignore(); + segment = null; + } + } + +} diff --git a/instrumentation/r2dbc-mariadb-1.2.1/src/main/java/com/nr/agent/instrumentation/r2dbc/R2dbcUtils.java b/instrumentation/r2dbc-mariadb-1.2.1/src/main/java/com/nr/agent/instrumentation/r2dbc/R2dbcUtils.java new file mode 100644 index 0000000000..e4257e5747 --- /dev/null +++ b/instrumentation/r2dbc-mariadb-1.2.1/src/main/java/com/nr/agent/instrumentation/r2dbc/R2dbcUtils.java @@ -0,0 +1,61 @@ +package com.nr.agent.instrumentation.r2dbc; + +import com.newrelic.agent.bridge.NoOpTransaction; +import com.newrelic.agent.bridge.datastore.OperationAndTableName; +import com.newrelic.agent.bridge.datastore.R2dbcObfuscator; +import com.newrelic.agent.bridge.datastore.R2dbcOperation; +import com.newrelic.api.agent.DatastoreParameters; +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.Segment; +import com.newrelic.api.agent.Transaction; +import org.mariadb.r2dbc.api.MariadbResult; +import org.mariadb.r2dbc.client.Client; +import org.reactivestreams.Subscription; +import reactor.core.publisher.Flux; + +import java.util.function.Consumer; + +public class R2dbcUtils { + public static Flux wrapRequest(Flux request, String sql, Client client) { + if(request != null) { + Transaction transaction = NewRelic.getAgent().getTransaction(); + if(transaction != null && !(transaction instanceof NoOpTransaction)) { + Segment segment = transaction.startSegment("execute"); + return request + .doOnSubscribe(reportExecution(sql, client, segment)) + .doFinally((type) -> segment.end()); + } + } + return request; + } + + private static Consumer reportExecution(String sql, Client client, Segment segment) { + return (subscription) -> { + OperationAndTableName sqlOperation = R2dbcOperation.extractFrom(sql); + if (sqlOperation != null) { + segment.reportAsExternal(DatastoreParameters + .product("MariaDB") + .collection(sqlOperation.getTableName()) + .operation(sqlOperation.getOperation()) + .instance(client.getHostAddress().getHost(), client.getHostAddress().getPort()) + .databaseName(client.getContext().getDatabase()) + .slowQuery(sql, R2dbcObfuscator.MYSQL_QUERY_CONVERTER) + .build()); + } + }; + } + + public static DatastoreParameters getParams(String sql, Client client) { + OperationAndTableName sqlOperation = R2dbcOperation.extractFrom(sql); + if(sqlOperation == null) return null; + return DatastoreParameters + .product("MariaDB") + .collection(sqlOperation.getTableName()) + .operation(sqlOperation.getOperation()) + .instance(client.getHostAddress().getHost(), client.getHostAddress().getPort()) + .databaseName(client.getContext().getDatabase()) + .slowQuery(sql, R2dbcObfuscator.MYSQL_QUERY_CONVERTER) + .build(); + } + +} diff --git a/instrumentation/r2dbc-mariadb-1.2.1/src/main/java/org/mariadb/r2dbc/MariadbClientParameterizedQueryStatement_Instrumentation.java b/instrumentation/r2dbc-mariadb-1.2.1/src/main/java/org/mariadb/r2dbc/MariadbClientParameterizedQueryStatement_Instrumentation.java new file mode 100644 index 0000000000..52c8f765f3 --- /dev/null +++ b/instrumentation/r2dbc-mariadb-1.2.1/src/main/java/org/mariadb/r2dbc/MariadbClientParameterizedQueryStatement_Instrumentation.java @@ -0,0 +1,35 @@ +package org.mariadb.r2dbc; + +import org.mariadb.r2dbc.api.MariadbResult; +import org.mariadb.r2dbc.client.Client; + +import com.newrelic.api.agent.DatastoreParameters; +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.Segment; +import com.newrelic.api.agent.Trace; +import com.newrelic.api.agent.weaver.MatchType; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import com.nr.agent.instrumentation.r2dbc.CancelHandler; +import com.nr.agent.instrumentation.r2dbc.NRHolder; +import com.nr.agent.instrumentation.r2dbc.R2dbcUtils; + +import reactor.core.publisher.Flux; + +@Weave(type = MatchType.ExactClass, originalName = "org.mariadb.r2dbc.MariadbClientParameterizedQueryStatement") +abstract class MariadbClientParameterizedQueryStatement_Instrumentation extends MariadbCommonStatement { + + MariadbClientParameterizedQueryStatement_Instrumentation( + Client client, String sql, MariadbConnectionConfiguration configuration) { + super(client, sql, configuration); + } + + @Trace(dispatcher = true) + public Flux execute() { + Flux request = Weaver.callOriginal(); + DatastoreParameters params = R2dbcUtils.getParams(initialSql, client); + Segment segment = NewRelic.getAgent().getTransaction().startSegment("execute"); + NRHolder holder = new NRHolder(segment, params); + return request.doOnError(holder).doOnTerminate(holder).doOnCancel(new CancelHandler(holder)); + } +} diff --git a/instrumentation/r2dbc-mariadb-1.2.1/src/main/java/org/mariadb/r2dbc/MariadbServerParameterizedQueryStatement_Instrumentation.java b/instrumentation/r2dbc-mariadb-1.2.1/src/main/java/org/mariadb/r2dbc/MariadbServerParameterizedQueryStatement_Instrumentation.java new file mode 100644 index 0000000000..7164575f4c --- /dev/null +++ b/instrumentation/r2dbc-mariadb-1.2.1/src/main/java/org/mariadb/r2dbc/MariadbServerParameterizedQueryStatement_Instrumentation.java @@ -0,0 +1,33 @@ +package org.mariadb.r2dbc; + +import org.mariadb.r2dbc.api.MariadbResult; +import org.mariadb.r2dbc.client.Client; + +import com.newrelic.api.agent.DatastoreParameters; +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.Segment; +import com.newrelic.api.agent.weaver.MatchType; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import com.nr.agent.instrumentation.r2dbc.CancelHandler; +import com.nr.agent.instrumentation.r2dbc.NRHolder; +import com.nr.agent.instrumentation.r2dbc.R2dbcUtils; + +import reactor.core.publisher.Flux; + +@Weave(type = MatchType.ExactClass, originalName = "org.mariadb.r2dbc.MariadbServerParameterizedQueryStatement") +abstract class MariadbServerParameterizedQueryStatement_Instrumentation extends MariadbCommonStatement { + + MariadbServerParameterizedQueryStatement_Instrumentation( + Client client, String sql, MariadbConnectionConfiguration configuration) { + super(client, sql, configuration); + } + + public Flux execute() { + Flux request = Weaver.callOriginal(); + DatastoreParameters params = R2dbcUtils.getParams(initialSql, client); + Segment segment = NewRelic.getAgent().getTransaction().startSegment("execute"); + NRHolder holder = new NRHolder(segment, params); + return request.doOnError(holder).doOnTerminate(holder).doOnCancel(new CancelHandler(holder)); + } +} diff --git a/instrumentation/r2dbc-mariadb-1.2.1/src/test/java/com/nr/agent/instrumentation/r2dbc/MariadbInstrumentedTest.java b/instrumentation/r2dbc-mariadb-1.2.1/src/test/java/com/nr/agent/instrumentation/r2dbc/MariadbInstrumentedTest.java new file mode 100644 index 0000000000..6d8652a318 --- /dev/null +++ b/instrumentation/r2dbc-mariadb-1.2.1/src/test/java/com/nr/agent/instrumentation/r2dbc/MariadbInstrumentedTest.java @@ -0,0 +1,71 @@ +package com.nr.agent.instrumentation.r2dbc; + +import ch.vorburger.mariadb4j.DB; +import ch.vorburger.mariadb4j.DBConfigurationBuilder; +import com.newrelic.agent.introspec.DatastoreHelper; +import com.newrelic.agent.introspec.InstrumentationTestConfig; +import com.newrelic.agent.introspec.InstrumentationTestRunner; +import com.newrelic.agent.introspec.Introspector; +import io.r2dbc.spi.Connection; +import io.r2dbc.spi.ConnectionFactories; +import io.r2dbc.spi.ConnectionFactory; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import reactor.core.publisher.Mono; + +import static org.junit.Assert.assertEquals; + +@RunWith(InstrumentationTestRunner.class) +@InstrumentationTestConfig(includePrefixes = "org.mariadb.r2dbc") +public class MariadbInstrumentedTest { + + public static DB mariaDb; + public Connection connection; + + @Before + public void setup() throws Exception { + String databaseName = "MariaDB" + System.currentTimeMillis(); + DBConfigurationBuilder builder = DBConfigurationBuilder.newBuilder().setPort(0); + mariaDb = DB.newEmbeddedDB(builder.build()); + mariaDb.start(); + mariaDb.createDB(databaseName); + mariaDb.source("users.sql", "user", "password", databaseName); + ConnectionFactory connectionFactory = ConnectionFactories.get(builder.getURL(databaseName).replace("mysql", "mariadb").replace("jdbc", "r2dbc").replace("localhost", "user:password@localhost")); + connection = Mono.from(connectionFactory.create()).block(); + } + + @AfterClass + public static void teardown() throws Exception { + mariaDb.stop(); + } + + @Test + public void testSelect() { + //Given + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + DatastoreHelper helper = new DatastoreHelper("MariaDB"); + + //When + R2dbcTestUtils.basicRequests(connection); + + //Then + assertEquals(1, introspector.getFinishedTransactionCount(1000)); + assertEquals(1, introspector.getTransactionNames().size()); + String transactionName = introspector.getTransactionNames().stream().findFirst().orElse(""); + helper.assertScopedStatementMetricCount(transactionName, "INSERT", "USERS", 1); + helper.assertScopedStatementMetricCount(transactionName, "SELECT", "USERS", 3); + helper.assertScopedStatementMetricCount(transactionName, "UPDATE", "USERS", 1); + helper.assertScopedStatementMetricCount(transactionName, "DELETE", "USERS", 1); + helper.assertAggregateMetrics(); + helper.assertUnscopedOperationMetricCount("INSERT", 1); + helper.assertUnscopedOperationMetricCount("SELECT", 3); + helper.assertUnscopedOperationMetricCount("UPDATE", 1); + helper.assertUnscopedOperationMetricCount("DELETE", 1); + helper.assertUnscopedStatementMetricCount("INSERT", "USERS", 1); + helper.assertUnscopedStatementMetricCount("SELECT", "USERS", 3); + helper.assertUnscopedStatementMetricCount("UPDATE", "USERS", 1); + helper.assertUnscopedStatementMetricCount("DELETE", "USERS", 1); + } +} \ No newline at end of file diff --git a/instrumentation/r2dbc-mariadb-1.2.1/src/test/java/com/nr/agent/instrumentation/r2dbc/MariadbNoInstrumentationTest.java b/instrumentation/r2dbc-mariadb-1.2.1/src/test/java/com/nr/agent/instrumentation/r2dbc/MariadbNoInstrumentationTest.java new file mode 100644 index 0000000000..d53d163f07 --- /dev/null +++ b/instrumentation/r2dbc-mariadb-1.2.1/src/test/java/com/nr/agent/instrumentation/r2dbc/MariadbNoInstrumentationTest.java @@ -0,0 +1,62 @@ +package com.nr.agent.instrumentation.r2dbc; + +import ch.vorburger.mariadb4j.DB; +import ch.vorburger.mariadb4j.DBConfigurationBuilder; +import com.newrelic.agent.introspec.DatastoreHelper; +import com.newrelic.agent.introspec.InstrumentationTestConfig; +import com.newrelic.agent.introspec.InstrumentationTestRunner; +import com.newrelic.agent.introspec.Introspector; +import io.r2dbc.spi.Connection; +import io.r2dbc.spi.ConnectionFactories; +import io.r2dbc.spi.ConnectionFactory; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import reactor.core.publisher.Mono; + +import static org.junit.Assert.assertEquals; + +@RunWith(InstrumentationTestRunner.class) +@InstrumentationTestConfig(includePrefixes = "none") +public class MariadbNoInstrumentationTest { + + public static DB mariaDb; + public Connection connection; + + @Before + public void setup() throws Exception { + String databaseName = "MariaDB" + System.currentTimeMillis(); + DBConfigurationBuilder builder = DBConfigurationBuilder.newBuilder().setPort(0); + mariaDb = DB.newEmbeddedDB(builder.build()); + mariaDb.start(); + mariaDb.createDB(databaseName); + mariaDb.source("users.sql", "user", "password", databaseName); + ConnectionFactory connectionFactory = ConnectionFactories.get(builder.getURL(databaseName).replace("mysql", "mariadb").replace("jdbc", "r2dbc").replace("localhost", "user:password@localhost")); + connection = Mono.from(connectionFactory.create()).block(); + } + + @AfterClass + public static void teardown() throws Exception { + mariaDb.stop(); + } + + @Test + public void testSelect() { + //Given + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + DatastoreHelper helper = new DatastoreHelper("MariaDB"); + + //When + R2dbcTestUtils.basicRequests(connection); + + //Then + assertEquals(1, introspector.getFinishedTransactionCount(1000)); + assertEquals(1, introspector.getTransactionNames().size()); + String transactionName = introspector.getTransactionNames().stream().findFirst().orElse(""); + helper.assertScopedStatementMetricCount(transactionName, "INSERT", "USERS", 0); + helper.assertScopedStatementMetricCount(transactionName, "SELECT", "USERS", 0); + helper.assertScopedStatementMetricCount(transactionName, "UPDATE", "USERS", 0); + helper.assertScopedStatementMetricCount(transactionName, "DELETE", "USERS", 0); + } +} \ No newline at end of file diff --git a/instrumentation/r2dbc-mariadb-1.2.1/src/test/java/com/nr/agent/instrumentation/r2dbc/R2dbcTestUtils.java b/instrumentation/r2dbc-mariadb-1.2.1/src/test/java/com/nr/agent/instrumentation/r2dbc/R2dbcTestUtils.java new file mode 100644 index 0000000000..f679e2b2a4 --- /dev/null +++ b/instrumentation/r2dbc-mariadb-1.2.1/src/test/java/com/nr/agent/instrumentation/r2dbc/R2dbcTestUtils.java @@ -0,0 +1,85 @@ +package com.nr.agent.instrumentation.r2dbc; + +import java.util.List; + +import org.reactivestreams.Publisher; + +import com.newrelic.api.agent.Trace; + +import io.r2dbc.spi.Connection; +import io.r2dbc.spi.Result; +import io.r2dbc.spi.Statement; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +public class R2dbcTestUtils { + + @Trace(dispatcher = true) + public static void basicRequests(Connection connection) { + Statement stmt = connection.createStatement("INSERT INTO USERS(id, first_name, last_name, age) VALUES(1, 'Max', 'Power', 30)"); + Publisher publish = stmt.execute(); + Long i = Flux.from(publish).flatMap(it -> it.getRowsUpdated()).blockLast(); + System.out.println("Inserted " + i + " row(s)"); + publish = connection.createStatement("SELECT * FROM USERS WHERE last_name='Power'").execute(); + Mono> users = Flux.from(publish).flatMap(it -> it.map((row,rowMetadata) -> + new User(row.get("first_name", String.class),row.get("last_name", String.class),row.get("age", Integer.class)))).collectList(); + List userList = users.block(); + System.out.println("Select returned " + userList.size() + " users"); + int count = 1; + for(User user : userList) { + + System.out.println("user " + count + ": " + user); + count++; + } + + publish = connection.createStatement("UPDATE USERS SET age = 36 WHERE last_name = 'Power'").execute(); + i = Flux.from(publish).flatMap(it -> it.getRowsUpdated()).blockLast(); + System.out.println("Updated ages of " + i + " users with last name Power"); + + publish = connection.createStatement("SELECT * FROM USERS WHERE last_name='Power'").execute(); + users = Flux.from(publish).flatMap(it -> it.map((row,rowMetadata) -> + new User(row.get("first_name", String.class),row.get("last_name", String.class),row.get("age", Integer.class)))).collectList(); + userList = users.block(); + System.out.println("Select returned " + userList.size() + " users"); + count = 1; + for(User user : userList) { + + System.out.println("user " + count + ": " + user); + count++; + } + + + + publish = connection.createStatement("DELETE FROM USERS WHERE last_name = 'Power'").execute(); + i = Flux.from(publish).flatMap(it -> it.getRowsUpdated()).blockLast(); + System.out.println("Deleted " + i + " users with last name Power"); + + publish = connection.createStatement("SELECT * FROM USERS WHERE last_name='Power'").execute(); + users = Flux.from(publish).flatMap(it -> it.map((row,rowMetadata) -> + new User(row.get("first_name", String.class),row.get("last_name", String.class),row.get("age", Integer.class)))).collectList(); + userList = users.block(); + System.out.println("Select returned " + userList.size() + " users"); + count = 1; + for(User user : userList) { + + System.out.println("user " + count + ": " + user); + count++; + } + } + + public static class User { + String first; + String last; + int age; + + public User(String fn, String ln, int a) { + first = fn; + last = ln; + age = a; + } + + public String toString() { + return "Name: " + first + " " + last + ", age: " + age; + } + } +} diff --git a/instrumentation/r2dbc-mariadb-1.2.1/src/test/resources/users.sql b/instrumentation/r2dbc-mariadb-1.2.1/src/test/resources/users.sql new file mode 100644 index 0000000000..6380228532 --- /dev/null +++ b/instrumentation/r2dbc-mariadb-1.2.1/src/test/resources/users.sql @@ -0,0 +1,2 @@ +CREATE TABLE IF NOT EXISTS USERS(id int primary key, first_name varchar(255), last_name varchar(255), age int); +TRUNCATE TABLE USERS \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index e459508a1e..685d6d267a 100644 --- a/settings.gradle +++ b/settings.gradle @@ -189,6 +189,7 @@ include 'instrumentation:jdbc-jtds' include 'instrumentation:jdbc-jtds-generic' include 'instrumentation:jdbc-mariadb-1.1.7' include 'instrumentation:jdbc-mariadb-1.3.0' +include 'instrumentation:jdbc-mariadb-3.0.0' include 'instrumentation:jdbc-mysql-3.0.8' include 'instrumentation:jdbc-mysql-6.0.2' include 'instrumentation:jdbc-mysql-8.0.11' @@ -311,6 +312,8 @@ include 'instrumentation:ning-async-http-client-1.1' include 'instrumentation:ning-async-http-client-1.6.1' include 'instrumentation:r2dbc-h2' include 'instrumentation:r2dbc-mariadb' +include 'instrumentation:r2dbc-mariadb-1.1.2' +include 'instrumentation:r2dbc-mariadb-1.2.1' include 'instrumentation:r2dbc-mysql' include 'instrumentation:r2dbc-postgresql-0.9.0' include 'instrumentation:r2dbc-postgresql-0.9.2'