Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@

package org.apache.druid.metadata.storage.mysql;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Joiner;
import com.google.common.base.Supplier;
import com.google.common.collect.ImmutableList;
Expand Down Expand Up @@ -62,10 +61,10 @@ public MySQLConnector(
{
super(config, dbTables);
log.info("Loading \"MySQL\" metadata connector driver %s", driverConfig.getDriverClassName());
tryLoadDriverClass(driverConfig.getDriverClassName());
tryLoadDriverClass(driverConfig.getDriverClassName(), true);

if (driverConfig.getDriverClassName().contains("mysql")) {
myTransientExceptionClass = tryLoadDriverClass(MYSQL_TRANSIENT_EXCEPTION_CLASS_NAME);
myTransientExceptionClass = tryLoadDriverClass(MYSQL_TRANSIENT_EXCEPTION_CLASS_NAME, false);
} else {
myTransientExceptionClass = null;
}
Expand Down Expand Up @@ -205,7 +204,11 @@ public boolean tableExists(Handle handle, String tableName)
@Override
protected boolean connectorIsTransientException(Throwable e)
{
return isTransientException(myTransientExceptionClass, e);
if (myTransientExceptionClass != null) {
return myTransientExceptionClass.isAssignableFrom(e.getClass())
|| e instanceof SQLException && ((SQLException) e).getErrorCode() == 1317 /* ER_QUERY_INTERRUPTED */;
}
return false;
}

@Override
Expand Down Expand Up @@ -241,28 +244,23 @@ public DBI getDBI()
return dbi;
}

private Class<?> tryLoadDriverClass(String className)
@Nullable
private Class<?> tryLoadDriverClass(String className, boolean failIfNotFound)
{
try {
return Class.forName(className, false, getClass().getClassLoader());
}
catch (ClassNotFoundException e) {
throw new ISE(e, "Could not find %s on the classpath. The MySQL Connector library is not included in the Druid "
+ "distribution but is required to use MySQL. Please download a compatible library (for example "
+ "'mysql-connector-java-5.1.48.jar') and place it under 'extensions/mysql-metadata-storage/'. See "
+ "https://druid.apache.org/downloads for more details.",
className
);
}
}

@VisibleForTesting
static boolean isTransientException(@Nullable Class<?> driverClazz, Throwable e)
{
if (driverClazz != null) {
return driverClazz.isAssignableFrom(e.getClass())
|| e instanceof SQLException && ((SQLException) e).getErrorCode() == 1317 /* ER_QUERY_INTERRUPTED */;
if (failIfNotFound) {
throw new ISE(e, "Could not find %s on the classpath. The MySQL Connector library is not included in the Druid "
+ "distribution but is required to use MySQL. Please download a compatible library (for example "
+ "'mysql-connector-java-5.1.48.jar') and place it under 'extensions/mysql-metadata-storage/'. See "
+ "https://druid.apache.org/downloads for more details.",
className
);
}
log.warn("Could not find %s on the classpath.", className);
return null;
}
return false;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,11 @@

package org.apache.druid.metadata.storage.mysql;

import com.google.common.base.Supplier;
import com.mysql.jdbc.exceptions.MySQLTransactionRollbackException;
import com.mysql.jdbc.exceptions.MySQLTransientException;
import org.apache.druid.metadata.MetadataStorageConnectorConfig;
import org.apache.druid.metadata.MetadataStorageTablesConfig;
import org.junit.Assert;
import org.junit.Test;

Expand All @@ -29,37 +32,63 @@

public class MySQLConnectorTest
{
private static final MySQLConnectorDriverConfig MYSQL_DRIVER_CONFIG = new MySQLConnectorDriverConfig();
private static final MySQLConnectorDriverConfig MARIADB_DRIVER_CONFIG = new MySQLConnectorDriverConfig()
{
@Override
public String getDriverClassName()
{
return "org.mariadb.jdbc.Driver";
}
};
private static final Supplier<MetadataStorageConnectorConfig> CONNECTOR_CONFIG_SUPPLIER =
MetadataStorageConnectorConfig::new;
private static final Supplier<MetadataStorageTablesConfig> TABLES_CONFIG_SUPPLIER =
() -> new MetadataStorageTablesConfig(null, null, null, null, null, null, null, null, null, null, null);


@Test
public void testIsExceptionTransient()
public void testIsExceptionTransientMySql()
{
final Class<?> clazz = MySQLTransientException.class;
Assert.assertTrue(MySQLConnector.isTransientException(clazz, new MySQLTransientException()));
Assert.assertTrue(MySQLConnector.isTransientException(clazz, new MySQLTransactionRollbackException()));
MySQLConnector connector = new MySQLConnector(
CONNECTOR_CONFIG_SUPPLIER,
TABLES_CONFIG_SUPPLIER,
new MySQLConnectorSslConfig(),
MYSQL_DRIVER_CONFIG
);
Assert.assertTrue(connector.connectorIsTransientException(new MySQLTransientException()));
Assert.assertTrue(connector.connectorIsTransientException(new MySQLTransactionRollbackException()));
Assert.assertTrue(
MySQLConnector.isTransientException(clazz, new SQLException("some transient failure", "wtf", 1317))
connector.connectorIsTransientException(new SQLException("some transient failure", "wtf", 1317))
);
Assert.assertFalse(
MySQLConnector.isTransientException(clazz, new SQLException("totally realistic test data", "wtf", 1337))
connector.connectorIsTransientException(new SQLException("totally realistic test data", "wtf", 1337))
);
// this method does not specially handle normal transient exceptions either, since it is not vendor specific
Assert.assertFalse(
MySQLConnector.isTransientException(clazz, new SQLTransientConnectionException("transient"))
connector.connectorIsTransientException(new SQLTransientConnectionException("transient"))
);
}

@Test
public void testIsExceptionTransientNoMySqlClazz()
{
MySQLConnector connector = new MySQLConnector(
CONNECTOR_CONFIG_SUPPLIER,
TABLES_CONFIG_SUPPLIER,
new MySQLConnectorSslConfig(),
MARIADB_DRIVER_CONFIG
);
// no vendor specific for MariaDb, so should always be false
Assert.assertFalse(MySQLConnector.isTransientException(null, new MySQLTransientException()));
Assert.assertFalse(connector.connectorIsTransientException(new MySQLTransientException()));
Assert.assertFalse(
MySQLConnector.isTransientException(null, new SQLException("some transient failure", "wtf", 1317))
connector.connectorIsTransientException(new SQLException("some transient failure", "wtf", 1317))
);
Assert.assertFalse(
MySQLConnector.isTransientException(null, new SQLException("totally realistic test data", "wtf", 1337))
connector.connectorIsTransientException(new SQLException("totally realistic test data", "wtf", 1337))
);
Assert.assertFalse(
MySQLConnector.isTransientException(null, new SQLTransientConnectionException("transient"))
connector.connectorIsTransientException(new SQLTransientConnectionException("transient"))
);
}
}