Skip to content

Commit 9e3756e

Browse files
committed
fix issue with custom domains during failover
1 parent 2daf268 commit 9e3756e

File tree

6 files changed

+88
-39
lines changed

6 files changed

+88
-39
lines changed

wrapper/src/main/java/software/amazon/jdbc/dialect/DialectManager.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import software.amazon.jdbc.Driver;
3030
import software.amazon.jdbc.HostSpec;
3131
import software.amazon.jdbc.PluginService;
32+
import software.amazon.jdbc.PropertyDefinition;
3233
import software.amazon.jdbc.util.CacheMap;
3334
import software.amazon.jdbc.util.ConnectionUrlParser;
3435
import software.amazon.jdbc.util.Messages;
@@ -81,7 +82,11 @@ public class DialectManager implements DialectProvider {
8182
private Dialect dialect = null;
8283
private String dialectCode;
8384

84-
private PluginService pluginService;
85+
private final PluginService pluginService;
86+
87+
static {
88+
PropertyDefinition.registerPluginProperties(DialectManager.class);
89+
}
8590

8691
public DialectManager(PluginService pluginService) {
8792
this.pluginService = pluginService;

wrapper/src/main/java/software/amazon/jdbc/plugin/AuroraConnectionTrackerPlugin.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ public class AuroraConnectionTrackerPlugin extends AbstractConnectionPlugin impl
4949
Collections.unmodifiableSet(new HashSet<String>() {
5050
{
5151
addAll(SubscribedMethodHelper.NETWORK_BOUND_METHODS);
52+
add(METHOD_CLOSE);
53+
add(METHOD_ABORT);
5254
add("connect");
5355
add("notifyNodeListChanged");
5456
}
@@ -87,7 +89,7 @@ public Connection connect(final String driverProtocol, final HostSpec hostSpec,
8789

8890
if (conn != null) {
8991
final RdsUrlType type = this.rdsHelper.identifyRdsType(hostSpec.getHost());
90-
if (type.isRdsCluster()) {
92+
if (type.isRdsCluster() || type == RdsUrlType.OTHER) {
9193
hostSpec.resetAliases();
9294
this.pluginService.fillAliases(conn, hostSpec);
9395
}

wrapper/src/main/java/software/amazon/jdbc/plugin/AuroraInitialConnectionStrategyPlugin.java

Lines changed: 40 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,14 @@
1919
import java.sql.Connection;
2020
import java.sql.SQLException;
2121
import java.util.Collections;
22+
import java.util.HashMap;
2223
import java.util.HashSet;
24+
import java.util.Map;
2325
import java.util.Properties;
2426
import java.util.Set;
2527
import java.util.concurrent.TimeUnit;
2628
import java.util.logging.Logger;
29+
import java.util.regex.Pattern;
2730
import software.amazon.jdbc.AwsWrapperProperty;
2831
import software.amazon.jdbc.HostListProviderService;
2932
import software.amazon.jdbc.HostRole;
@@ -32,9 +35,11 @@
3235
import software.amazon.jdbc.PluginService;
3336
import software.amazon.jdbc.PropertyDefinition;
3437
import software.amazon.jdbc.hostavailability.HostAvailability;
38+
import software.amazon.jdbc.plugin.failover.FailoverMode;
3539
import software.amazon.jdbc.util.Messages;
3640
import software.amazon.jdbc.util.RdsUrlType;
3741
import software.amazon.jdbc.util.RdsUtils;
42+
import software.amazon.jdbc.util.StringUtils;
3843
import software.amazon.jdbc.util.WrapperUtils;
3944

4045
public class AuroraInitialConnectionStrategyPlugin extends AbstractConnectionPlugin {
@@ -67,16 +72,47 @@ public class AuroraInitialConnectionStrategyPlugin extends AbstractConnectionPlu
6772
"1000",
6873
"Time between each retry of opening a connection.");
6974

75+
public static final AwsWrapperProperty VERIFY_OPENED_CONNECTION_TYPE =
76+
new AwsWrapperProperty(
77+
"verifyOpenedConnectionType",
78+
null,
79+
"Force to verify an opened connection to be either a writer or a reader.");
80+
81+
private enum VerifyOpenedConnectionType {
82+
WRITER,
83+
READER;
84+
85+
private static final Map<String, VerifyOpenedConnectionType> nameToValue =
86+
new HashMap<String, VerifyOpenedConnectionType>() {
87+
{
88+
put("writer", WRITER);
89+
put("reader", READER);
90+
}
91+
};
92+
93+
public static VerifyOpenedConnectionType fromValue(String value) {
94+
if (value == null) {
95+
return null;
96+
}
97+
return nameToValue.get(value.toLowerCase());
98+
}
99+
}
100+
70101
private final PluginService pluginService;
71102
private HostListProviderService hostListProviderService;
72103
private final RdsUtils rdsUtils = new RdsUtils();
73104

105+
private VerifyOpenedConnectionType verifyOpenedConnectionType = null;
106+
107+
74108
static {
75109
PropertyDefinition.registerPluginProperties(AuroraInitialConnectionStrategyPlugin.class);
76110
}
77111

78112
public AuroraInitialConnectionStrategyPlugin(final PluginService pluginService, final Properties properties) {
79113
this.pluginService = pluginService;
114+
this.verifyOpenedConnectionType =
115+
VerifyOpenedConnectionType.fromValue(VERIFY_OPENED_CONNECTION_TYPE.getString(properties));
80116
}
81117

82118
@Override
@@ -110,12 +146,8 @@ public Connection connect(
110146

111147
final RdsUrlType type = this.rdsUtils.identifyRdsType(hostSpec.getHost());
112148

113-
if (!type.isRdsCluster()) {
114-
// It's not a cluster endpoint. Continue with a normal workflow.
115-
return connectFunc.call();
116-
}
117-
118-
if (type == RdsUrlType.RDS_WRITER_CLUSTER) {
149+
if (type == RdsUrlType.RDS_WRITER_CLUSTER
150+
|| this.verifyOpenedConnectionType == VerifyOpenedConnectionType.WRITER) {
119151
Connection writerCandidateConn = this.getVerifiedWriterConnection(props, isInitialConnection, connectFunc);
120152
if (writerCandidateConn == null) {
121153
// Can't get writer connection. Continue with a normal workflow.
@@ -124,7 +156,8 @@ public Connection connect(
124156
return writerCandidateConn;
125157
}
126158

127-
if (type == RdsUrlType.RDS_READER_CLUSTER) {
159+
if (type == RdsUrlType.RDS_READER_CLUSTER
160+
|| this.verifyOpenedConnectionType == VerifyOpenedConnectionType.READER) {
128161
Connection readerCandidateConn = this.getVerifiedReaderConnection(props, isInitialConnection, connectFunc);
129162
if (readerCandidateConn == null) {
130163
// Can't get a reader connection. Continue with a normal workflow.

wrapper/src/main/java/software/amazon/jdbc/plugin/OpenedConnectionTracker.java

Lines changed: 39 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,14 @@
2020
import java.sql.Connection;
2121
import java.sql.SQLException;
2222
import java.util.Arrays;
23+
import java.util.HashSet;
2324
import java.util.Map;
2425
import java.util.Objects;
25-
import java.util.Optional;
2626
import java.util.Queue;
2727
import java.util.Set;
2828
import java.util.concurrent.ConcurrentHashMap;
2929
import java.util.concurrent.ConcurrentLinkedQueue;
30+
import java.util.concurrent.Executor;
3031
import java.util.concurrent.ExecutorService;
3132
import java.util.concurrent.Executors;
3233
import java.util.logging.Logger;
@@ -35,6 +36,7 @@
3536
import software.amazon.jdbc.util.Messages;
3637
import software.amazon.jdbc.util.RdsUtils;
3738
import software.amazon.jdbc.util.StringUtils;
39+
import software.amazon.jdbc.util.SynchronousExecutor;
3840
import software.amazon.jdbc.util.telemetry.TelemetryContext;
3941
import software.amazon.jdbc.util.telemetry.TelemetryFactory;
4042
import software.amazon.jdbc.util.telemetry.TelemetryTraceLevel;
@@ -50,16 +52,17 @@ public class OpenedConnectionTracker {
5052
invalidateThread.setDaemon(true);
5153
return invalidateThread;
5254
});
53-
private static final ExecutorService abortConnectionExecutorService =
54-
Executors.newCachedThreadPool(
55-
r -> {
56-
final Thread abortThread = new Thread(r);
57-
abortThread.setDaemon(true);
58-
return abortThread;
59-
});
55+
private static final Executor abortConnectionExecutor = new SynchronousExecutor();
6056

6157
private static final Logger LOGGER = Logger.getLogger(OpenedConnectionTracker.class.getName());
6258
private static final RdsUtils rdsUtils = new RdsUtils();
59+
60+
private static final Set<String> safeCheckIfClosed = new HashSet<>(Arrays.asList(
61+
"HikariProxyConnection",
62+
"org.postgresql.jdbc.PgConnection",
63+
"com.mysql.cj.jdbc.ConnectionImpl",
64+
"org.mariadb.jdbc.Connection"));
65+
6366
private final PluginService pluginService;
6467

6568
public OpenedConnectionTracker(final PluginService pluginService) {
@@ -72,6 +75,7 @@ public void populateOpenedConnectionQueue(final HostSpec hostSpec, final Connect
7275
// Check if the connection was established using an instance endpoint
7376
if (rdsUtils.isRdsInstance(hostSpec.getHost())) {
7477
trackConnection(hostSpec.getHostAndPort(), conn);
78+
logOpenedConnections();
7579
return;
7680
}
7781

@@ -80,14 +84,17 @@ public void populateOpenedConnectionQueue(final HostSpec hostSpec, final Connect
8084
.max(String::compareToIgnoreCase)
8185
.orElse(null);
8286

83-
if (instanceEndpoint == null) {
84-
LOGGER.finest(
85-
Messages.get("OpenedConnectionTracker.unableToPopulateOpenedConnectionQueue",
86-
new Object[] {hostSpec.getHost()}));
87+
if (instanceEndpoint != null) {
88+
trackConnection(instanceEndpoint, conn);
89+
logOpenedConnections();
8790
return;
8891
}
8992

90-
trackConnection(instanceEndpoint, conn);
93+
// It seems there's no RDS instance host found. It might be a custom domain name. Let's track by all aliases
94+
for (String alias : aliases) {
95+
trackConnection(alias, conn);
96+
}
97+
logOpenedConnections();
9198
}
9299

93100
/**
@@ -100,22 +107,21 @@ public void invalidateAllConnections(final HostSpec hostSpec) {
100107
invalidateAllConnections(hostSpec.getAliases().toArray(new String[] {}));
101108
}
102109

103-
public void invalidateAllConnections(final String... node) {
110+
public void invalidateAllConnections(final String... keys) {
104111
TelemetryFactory telemetryFactory = this.pluginService.getTelemetryFactory();
105112
TelemetryContext telemetryContext = telemetryFactory.openTelemetryContext(
106113
TELEMETRY_INVALIDATE_CONNECTIONS, TelemetryTraceLevel.NESTED);
107114

108115
try {
109-
final Optional<String> instanceEndpoint = Arrays.stream(node)
110-
.filter(x -> rdsUtils.isRdsInstance(rdsUtils.removePort(x)))
111-
.findFirst();
112-
if (!instanceEndpoint.isPresent()) {
113-
return;
116+
for (String key : keys) {
117+
try {
118+
final Queue<WeakReference<Connection>> connectionQueue = openedConnections.get(key);
119+
logConnectionQueue(key, connectionQueue);
120+
invalidateConnections(connectionQueue);
121+
} catch (Exception ex) {
122+
// ignore and continue
123+
}
114124
}
115-
final Queue<WeakReference<Connection>> connectionQueue = openedConnections.get(instanceEndpoint.get());
116-
logConnectionQueue(instanceEndpoint.get(), connectionQueue);
117-
invalidateConnections(openedConnections.get(instanceEndpoint.get()));
118-
119125
} finally {
120126
telemetryContext.closeContext();
121127
}
@@ -134,8 +140,11 @@ public void invalidateCurrentConnection(final HostSpec hostSpec, final Connectio
134140
}
135141

136142
final Queue<WeakReference<Connection>> connectionQueue = openedConnections.get(host);
137-
logConnectionQueue(host, connectionQueue);
138-
connectionQueue.removeIf(connectionWeakReference -> Objects.equals(connectionWeakReference.get(), connection));
143+
if (connectionQueue != null) {
144+
logConnectionQueue(host, connectionQueue);
145+
connectionQueue.removeIf(connectionWeakReference -> connectionWeakReference != null
146+
&& Objects.equals(connectionWeakReference.get(), connection));
147+
}
139148
}
140149

141150
private void trackConnection(final String instanceEndpoint, final Connection connection) {
@@ -144,7 +153,6 @@ private void trackConnection(final String instanceEndpoint, final Connection con
144153
instanceEndpoint,
145154
(k) -> new ConcurrentLinkedQueue<>());
146155
connectionQueue.add(new WeakReference<>(connection));
147-
logOpenedConnections();
148156
}
149157

150158
private void invalidateConnections(final Queue<WeakReference<Connection>> connectionQueue) {
@@ -157,7 +165,7 @@ private void invalidateConnections(final Queue<WeakReference<Connection>> connec
157165
}
158166

159167
try {
160-
conn.abort(abortConnectionExecutorService);
168+
conn.abort(abortConnectionExecutor);
161169
} catch (final SQLException e) {
162170
// swallow this exception, current connection should be useless anyway.
163171
}
@@ -204,7 +212,10 @@ public void pruneNullConnections() {
204212
if (conn == null) {
205213
return true;
206214
}
207-
if (conn.getClass().getSimpleName().equals("HikariProxyConnection")) {
215+
// The following classes do not check connection validity by calling a DB server
216+
// so it's safe to check whether connection is already closed.
217+
if (safeCheckIfClosed.contains(conn.getClass().getSimpleName())
218+
|| safeCheckIfClosed.contains(conn.getClass().getName())) {
208219
try {
209220
return conn.isClosed();
210221
} catch (SQLException ex) {

wrapper/src/main/java/software/amazon/jdbc/targetdriverdialect/MysqlConnectorJDriverHelper.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616

1717
package software.amazon.jdbc.targetdriverdialect;
1818

19-
import com.mysql.cj.jdbc.MysqlConnectionPoolDataSource;
2019
import com.mysql.cj.jdbc.MysqlDataSource;
2120
import java.sql.DriverManager;
2221
import java.sql.SQLException;

wrapper/src/main/resources/aws_advanced_jdbc_wrapper_messages.properties

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -363,7 +363,6 @@ AuroraStaleDnsHelper.staleDnsDetected=Stale DNS data detected. Opening a connect
363363
AuroraStaleDnsHelper.reset=Reset stored writer host.
364364

365365
# Opened Connection Tracker
366-
OpenedConnectionTracker.unableToPopulateOpenedConnectionQueue=The driver is unable to track this opened connection because the instance endpoint is unknown: ''{0}''
367366
OpenedConnectionTracker.invalidatingConnections=Invalidating opened connections to host: ''{0}''
368367

369368
# Util

0 commit comments

Comments
 (0)