From 7ea24f545f5b88c6466b054a1994be9590ccf8bb Mon Sep 17 00:00:00 2001 From: Manyanda Chitimbo Date: Sat, 21 Mar 2020 19:48:29 +0100 Subject: [PATCH] fix(jdbc-mysql): MySQL high availability not working in native mode Fixes https://github.com/quarkusio/quarkus/issues/7936 I was able to reproduce the error and adding the missing proxy registration fixed it. To test the fix, using the below mysql replication setup: ``` docker run \ --name mysql_primary \ -p 3306:3306 \ -e MYSQL_ROOT_PASSWORD=hibernate_orm_test \ -e MYSQL_USER=hibernate_orm_test \ -e MYSQL_PASSWORD=hibernate_orm_test \ -e MYSQL_DATABASE=hibernate_orm_test \ -e REPLICATION_USER=hibernate_orm_test \ -e REPLICATION_PASSWORD=hibernate_orm_test \ actency/docker-mysql-replication:5.7 ``` And on another terminal tab, run the following command: ``` docker run \ --name mysql_secondary \ -p 3307:3306 \ -e MYSQL_ROOT_PASSWORD=hibernate_orm_test \ -e MYSQL_USER=hibernate_orm_test \ -e MYSQL_PASSWORD=hibernate_orm_test \ -e MYSQL_DATABASE=hibernate_orm_test \ -e REPLICATION_USER=hibernate_orm_test \ -e REPLICATION_PASSWORD=hibernate_orm_test \ --link mysql_primary:master \ actency/docker-mysql-replication:5.7 ``` Open another terminal tab and launch jdbc-mysql integration tests with the following command `cd integration-tests/jpa-mysql` and then open the configuration file and edit the jdbc url so that it points to: ``` jdbc:mysql:replication://localhost:3306,localhost:3307/hibernate_orm_test?connectTimeout=5000&socketTimeout=5000&retriesAllDown=3&allowMasterDownConnections=true&allowSlaveDownConnections=true&loadBalanceBlacklistTimeout=10000&readFromMasterWhenNoSlaves=true` ``` Then running ``` mvn clean install -Dnative -Dtest-mysql ``` Should pass successfully which was not the case previously as it was throwing missing proxy registration errors --- .../mysql/deployment/JDBCMySQLProcessor.java | 46 +++++++++++++++++++ .../deployment/MySQLJDBCReflections.java | 43 ++++++++++++----- 2 files changed, 78 insertions(+), 11 deletions(-) diff --git a/extensions/jdbc/jdbc-mysql/deployment/src/main/java/io/quarkus/jdbc/mysql/deployment/JDBCMySQLProcessor.java b/extensions/jdbc/jdbc-mysql/deployment/src/main/java/io/quarkus/jdbc/mysql/deployment/JDBCMySQLProcessor.java index 52b6e777b060c..3455b3b728095 100644 --- a/extensions/jdbc/jdbc-mysql/deployment/src/main/java/io/quarkus/jdbc/mysql/deployment/JDBCMySQLProcessor.java +++ b/extensions/jdbc/jdbc-mysql/deployment/src/main/java/io/quarkus/jdbc/mysql/deployment/JDBCMySQLProcessor.java @@ -1,5 +1,24 @@ package io.quarkus.jdbc.mysql.deployment; +import java.io.Serializable; +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.Statement; +import java.util.ArrayList; +import java.util.List; + +import com.mysql.cj.MysqlConnection; +import com.mysql.cj.WarningListener; +import com.mysql.cj.conf.PropertySet; +import com.mysql.cj.jdbc.JdbcConnection; +import com.mysql.cj.jdbc.JdbcPreparedStatement; +import com.mysql.cj.jdbc.JdbcPropertySet; +import com.mysql.cj.jdbc.JdbcStatement; +import com.mysql.cj.jdbc.ha.LoadBalancedConnection; +import com.mysql.cj.jdbc.ha.ReplicationConnection; +import com.mysql.cj.jdbc.result.ResultSetInternalMethods; +import com.mysql.cj.protocol.Resultset; + import io.quarkus.agroal.deployment.JdbcDriverBuildItem; import io.quarkus.arc.deployment.AdditionalBeanBuildItem; import io.quarkus.arc.processor.BuiltinScope; @@ -13,6 +32,7 @@ import io.quarkus.deployment.builditem.NativeImageEnableAllCharsetsBuildItem; import io.quarkus.deployment.builditem.NativeImageEnableAllTimeZonesBuildItem; import io.quarkus.deployment.builditem.SslNativeConfigBuildItem; +import io.quarkus.deployment.builditem.nativeimage.NativeImageProxyDefinitionBuildItem; import io.quarkus.deployment.builditem.nativeimage.NativeImageResourceBuildItem; import io.quarkus.jdbc.mysql.runtime.MySQLAgroalConnectionConfigurer; import io.quarkus.jdbc.mysql.runtime.MySQLRecorder; @@ -62,4 +82,30 @@ NativeImageEnableAllCharsetsBuildItem enableAllCharsets() { NativeImageEnableAllTimeZonesBuildItem enableAllTimeZones() { return new NativeImageEnableAllTimeZonesBuildItem(); } + + @BuildStep + List registerProxies() { + List proxies = new ArrayList<>(); + proxies.add(new NativeImageProxyDefinitionBuildItem(JdbcConnection.class.getName())); + proxies.add(new NativeImageProxyDefinitionBuildItem(MysqlConnection.class.getName())); + proxies.add(new NativeImageProxyDefinitionBuildItem(Statement.class.getName())); + proxies.add(new NativeImageProxyDefinitionBuildItem(AutoCloseable.class.getName())); + proxies.add(new NativeImageProxyDefinitionBuildItem(JdbcStatement.class.getName())); + proxies.add(new NativeImageProxyDefinitionBuildItem(Connection.class.getName())); + proxies.add(new NativeImageProxyDefinitionBuildItem(ResultSet.class.getName())); + proxies.add( + new NativeImageProxyDefinitionBuildItem(JdbcPreparedStatement.class.getName(), JdbcStatement.class.getName())); + proxies.add(new NativeImageProxyDefinitionBuildItem(JdbcPropertySet.class.getName(), PropertySet.class.getName(), + Serializable.class.getName())); + proxies.add( + new NativeImageProxyDefinitionBuildItem(Resultset.class.getName(), ResultSetInternalMethods.class.getName())); + proxies.add(new NativeImageProxyDefinitionBuildItem(LoadBalancedConnection.class.getName(), + JdbcConnection.class.getName())); + proxies.add( + new NativeImageProxyDefinitionBuildItem(ReplicationConnection.class.getName(), JdbcConnection.class.getName())); + proxies.add( + new NativeImageProxyDefinitionBuildItem(ResultSetInternalMethods.class.getName(), + WarningListener.class.getName(), Resultset.class.getName())); + return proxies; + } } diff --git a/extensions/jdbc/jdbc-mysql/deployment/src/main/java/io/quarkus/jdbc/mysql/deployment/MySQLJDBCReflections.java b/extensions/jdbc/jdbc-mysql/deployment/src/main/java/io/quarkus/jdbc/mysql/deployment/MySQLJDBCReflections.java index a85da95ca6307..4dab5c595276c 100644 --- a/extensions/jdbc/jdbc-mysql/deployment/src/main/java/io/quarkus/jdbc/mysql/deployment/MySQLJDBCReflections.java +++ b/extensions/jdbc/jdbc-mysql/deployment/src/main/java/io/quarkus/jdbc/mysql/deployment/MySQLJDBCReflections.java @@ -1,5 +1,16 @@ package io.quarkus.jdbc.mysql.deployment; +import java.sql.Wrapper; + +import com.mysql.cj.conf.url.FailoverConnectionUrl; +import com.mysql.cj.conf.url.FailoverDnsSrvConnectionUrl; +import com.mysql.cj.conf.url.LoadBalanceConnectionUrl; +import com.mysql.cj.conf.url.LoadBalanceDnsSrvConnectionUrl; +import com.mysql.cj.conf.url.ReplicationConnectionUrl; +import com.mysql.cj.conf.url.ReplicationDnsSrvConnectionUrl; +import com.mysql.cj.conf.url.SingleConnectionUrl; +import com.mysql.cj.conf.url.XDevApiConnectionUrl; +import com.mysql.cj.conf.url.XDevApiDnsSrvConnectionUrl; import com.mysql.cj.exceptions.AssertionFailedException; import com.mysql.cj.exceptions.CJCommunicationsException; import com.mysql.cj.exceptions.CJConnectionFeatureNotAvailableException; @@ -26,6 +37,10 @@ import com.mysql.cj.exceptions.UnableToConnectException; import com.mysql.cj.exceptions.UnsupportedConnectionStringException; import com.mysql.cj.exceptions.WrongArgumentException; +import com.mysql.cj.jdbc.Driver; +import com.mysql.cj.jdbc.ha.NdbLoadBalanceExceptionChecker; +import com.mysql.cj.jdbc.ha.StandardLoadBalanceExceptionChecker; +import com.mysql.cj.log.StandardLogger; import com.mysql.cj.protocol.AsyncSocketFactory; import com.mysql.cj.protocol.NamedPipeSocketFactory; import com.mysql.cj.protocol.SocksProxySocketFactory; @@ -39,28 +54,30 @@ public final class MySQLJDBCReflections { @BuildStep void registerDriverForReflection(BuildProducer reflectiveClass) { - reflectiveClass.produce(new ReflectiveClassBuildItem(false, false, com.mysql.cj.jdbc.Driver.class.getName())); + + reflectiveClass.produce(new ReflectiveClassBuildItem(false, false, Driver.class.getName())); reflectiveClass.produce( - new ReflectiveClassBuildItem(false, false, com.mysql.cj.conf.url.FailoverDnsSrvConnectionUrl.class.getName())); + new ReflectiveClassBuildItem(false, false, FailoverDnsSrvConnectionUrl.class.getName())); reflectiveClass.produce( - new ReflectiveClassBuildItem(false, false, com.mysql.cj.conf.url.FailoverConnectionUrl.class.getName())); + new ReflectiveClassBuildItem(false, false, FailoverConnectionUrl.class.getName())); reflectiveClass - .produce(new ReflectiveClassBuildItem(false, false, com.mysql.cj.conf.url.SingleConnectionUrl.class.getName())); + .produce(new ReflectiveClassBuildItem(false, false, SingleConnectionUrl.class.getName())); reflectiveClass.produce( - new ReflectiveClassBuildItem(false, false, com.mysql.cj.conf.url.LoadBalanceConnectionUrl.class.getName())); + new ReflectiveClassBuildItem(false, false, LoadBalanceConnectionUrl.class.getName())); reflectiveClass.produce(new ReflectiveClassBuildItem(false, false, - com.mysql.cj.conf.url.LoadBalanceDnsSrvConnectionUrl.class.getName())); + LoadBalanceDnsSrvConnectionUrl.class.getName())); reflectiveClass.produce(new ReflectiveClassBuildItem(false, false, - com.mysql.cj.conf.url.ReplicationDnsSrvConnectionUrl.class.getName())); + ReplicationDnsSrvConnectionUrl.class.getName())); reflectiveClass.produce( - new ReflectiveClassBuildItem(false, false, com.mysql.cj.conf.url.ReplicationConnectionUrl.class.getName())); + new ReflectiveClassBuildItem(false, false, ReplicationConnectionUrl.class.getName())); reflectiveClass.produce( - new ReflectiveClassBuildItem(false, false, com.mysql.cj.conf.url.XDevApiConnectionUrl.class.getName())); + new ReflectiveClassBuildItem(false, false, XDevApiConnectionUrl.class.getName())); reflectiveClass.produce( - new ReflectiveClassBuildItem(false, false, com.mysql.cj.conf.url.XDevApiDnsSrvConnectionUrl.class.getName())); + new ReflectiveClassBuildItem(false, false, XDevApiDnsSrvConnectionUrl.class.getName())); reflectiveClass.produce(new ReflectiveClassBuildItem(false, false, com.mysql.cj.jdbc.ha.LoadBalancedAutoCommitInterceptor.class.getName())); - reflectiveClass.produce(new ReflectiveClassBuildItem(false, false, com.mysql.cj.log.StandardLogger.class.getName())); + reflectiveClass.produce(new ReflectiveClassBuildItem(false, false, StandardLogger.class.getName())); + reflectiveClass.produce(new ReflectiveClassBuildItem(false, false, Wrapper.class.getName())); } @BuildStep @@ -103,5 +120,9 @@ void registerExceptionsForReflection(BuildProducer ref reflectiveClass .produce(new ReflectiveClassBuildItem(false, false, UnsupportedConnectionStringException.class.getName())); reflectiveClass.produce(new ReflectiveClassBuildItem(false, false, WrongArgumentException.class.getName())); + reflectiveClass.produce(new ReflectiveClassBuildItem(true, true, "com.mysql.cj.jdbc.MysqlXAException")); + reflectiveClass + .produce(new ReflectiveClassBuildItem(false, false, StandardLoadBalanceExceptionChecker.class.getName())); + reflectiveClass.produce(new ReflectiveClassBuildItem(false, false, NdbLoadBalanceExceptionChecker.class.getName())); } }