monitorErrorResponses =
+ EnumSet.of(MonitorErrorResponse.RECREATE);
protected final FullServicesContainer servicesContainer;
protected final PluginService pluginService;
protected final LimitlessQueryHelper queryHelper;
diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/events/DataAccessEvent.java b/wrapper/src/main/java/software/amazon/jdbc/util/events/DataAccessEvent.java
index b94aadb40..81fe37147 100644
--- a/wrapper/src/main/java/software/amazon/jdbc/util/events/DataAccessEvent.java
+++ b/wrapper/src/main/java/software/amazon/jdbc/util/events/DataAccessEvent.java
@@ -24,8 +24,8 @@
* data.
*/
public class DataAccessEvent implements Event {
- protected @NonNull Class> dataClass;
- protected @NonNull Object key;
+ protected final @NonNull Class> dataClass;
+ protected final @NonNull Object key;
/**
* Constructor for a DataAccessEvent.
diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/events/Event.java b/wrapper/src/main/java/software/amazon/jdbc/util/events/Event.java
index 67c751cb4..170f989f2 100644
--- a/wrapper/src/main/java/software/amazon/jdbc/util/events/Event.java
+++ b/wrapper/src/main/java/software/amazon/jdbc/util/events/Event.java
@@ -18,6 +18,10 @@
/**
* An interface for events that need to be communicated between different components.
+ *
+ * All implementations of this interface MUST be immutable or use both the default {@link Object#equals} and
+ * {@link Object#hashCode} implementations, as instances will be used as keys in hash-based collections. Mutable
+ * implementations may cause undefined behavior when used as Map keys or Set elements.
*/
public interface Event {
boolean isImmediateDelivery();
diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/events/EventSubscriber.java b/wrapper/src/main/java/software/amazon/jdbc/util/events/EventSubscriber.java
index 34e63b452..877c7ef21 100644
--- a/wrapper/src/main/java/software/amazon/jdbc/util/events/EventSubscriber.java
+++ b/wrapper/src/main/java/software/amazon/jdbc/util/events/EventSubscriber.java
@@ -24,6 +24,10 @@
* {@link java.util.HashSet} to prevent duplicate subscriptions, so classes implementing this interface should consider
* whether they need to override {@link Object#equals(Object)} and {@link Object#hashCode()}.
*
+ *
All implementations of this interface MUST be immutable or use both the default {@link Object#equals} and
+ * {@link Object#hashCode} implementations, as instances will be used as keys in hash-based collections. Mutable
+ * implementations may cause undefined behavior when used as Map keys or Set elements.
+ *
* @see EventPublisher
*/
public interface EventSubscriber {
diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/AbstractMonitor.java b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/AbstractMonitor.java
index e7a92ffbd..269b76499 100644
--- a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/AbstractMonitor.java
+++ b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/AbstractMonitor.java
@@ -91,7 +91,6 @@ public void stop() {
Thread.currentThread().interrupt();
this.monitorExecutor.shutdownNow();
} finally {
- // TODO: Should this be removed? close() should be called in the run() method finally block
close();
this.state.set(MonitorState.STOPPED);
}
diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorService.java b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorService.java
index 18b950400..8b7ac7fe1 100644
--- a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorService.java
+++ b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorService.java
@@ -17,8 +17,8 @@
package software.amazon.jdbc.util.monitoring;
import java.sql.SQLException;
+import java.util.EnumSet;
import java.util.Properties;
-import java.util.Set;
import org.checkerframework.checker.nullness.qual.Nullable;
import software.amazon.jdbc.ConnectionProvider;
import software.amazon.jdbc.dialect.Dialect;
@@ -41,7 +41,8 @@ public interface MonitorService {
* @param heartbeatTimeoutNanos a duration in nanoseconds defining the maximum amount of time that a monitor should
* take between updating its last-updated timestamp. If a monitor has not updated its
* last-updated timestamp within this duration it will be considered stuck.
- * @param errorResponses a {@link Set} defining actions to take if the monitor is stuck or in an error state.
+ * @param errorResponses a {@link EnumSet} defining actions to take if the monitor is stuck or in an error
+ * state.
* @param producedDataClass the class of data produced by the monitor.
* @param the type of the monitor.
*/
@@ -49,7 +50,7 @@ void registerMonitorTypeIfAbsent(
Class monitorClass,
long expirationTimeoutNanos,
long heartbeatTimeoutNanos,
- Set errorResponses,
+ EnumSet errorResponses,
@Nullable Class> producedDataClass);
/**
diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java
index 95090b193..f6fb4da86 100644
--- a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java
+++ b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java
@@ -19,12 +19,12 @@
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
+import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;
-import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
@@ -59,8 +59,7 @@ public class MonitorServiceImpl implements MonitorService, EventSubscriber {
static {
Map, Supplier> suppliers = new HashMap<>();
- Set recreateOnError =
- new HashSet<>(Collections.singletonList(MonitorErrorResponse.RECREATE));
+ EnumSet recreateOnError = EnumSet.of(MonitorErrorResponse.RECREATE);
MonitorSettings defaultSettings = new MonitorSettings(
TimeUnit.MINUTES.toNanos(15), TimeUnit.MINUTES.toNanos(3), recreateOnError);
@@ -150,7 +149,7 @@ protected void handleMonitorError(
Monitor monitor = errorMonitorItem.getMonitor();
monitor.stop();
- Set errorResponses = cacheContainer.getSettings().getErrorResponses();
+ EnumSet errorResponses = cacheContainer.getSettings().getErrorResponses();
if (errorResponses != null && errorResponses.contains(MonitorErrorResponse.RECREATE)) {
cacheContainer.getCache().computeIfAbsent(key, k -> {
LOGGER.fine(Messages.get("MonitorServiceImpl.recreatingMonitor", new Object[] {monitor}));
@@ -166,7 +165,7 @@ public void registerMonitorTypeIfAbsent(
Class monitorClass,
long expirationTimeoutNanos,
long heartbeatTimeoutNanos,
- Set errorResponses,
+ EnumSet errorResponses,
@Nullable Class> producedDataClass) {
monitorCaches.computeIfAbsent(
monitorClass,
diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorSettings.java b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorSettings.java
index 6774058e8..4e181e058 100644
--- a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorSettings.java
+++ b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorSettings.java
@@ -16,6 +16,7 @@
package software.amazon.jdbc.util.monitoring;
+import java.util.EnumSet;
import java.util.Set;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
@@ -26,7 +27,7 @@
public class MonitorSettings {
private final long expirationTimeoutNanos;
private final long inactiveTimeoutNanos;
- private @Nullable final Set errorResponses;
+ private @Nullable final EnumSet errorResponses;
/**
* Constructs a MonitorSettings instance.
@@ -40,7 +41,7 @@ public class MonitorSettings {
* no action will be performed.
*/
public MonitorSettings(
- long expirationTimeoutNanos, long inactiveTimeoutNanos, @NonNull Set errorResponses) {
+ long expirationTimeoutNanos, long inactiveTimeoutNanos, @NonNull EnumSet errorResponses) {
this.expirationTimeoutNanos = expirationTimeoutNanos;
this.inactiveTimeoutNanos = inactiveTimeoutNanos;
this.errorResponses = errorResponses;
@@ -54,7 +55,7 @@ public long getInactiveTimeoutNanos() {
return inactiveTimeoutNanos;
}
- public @Nullable Set getErrorResponses() {
+ public @Nullable EnumSet getErrorResponses() {
return errorResponses;
}
}
diff --git a/wrapper/src/test/java/integration/TestEnvironmentRequest.java b/wrapper/src/test/java/integration/TestEnvironmentRequest.java
index 7e4b64daf..91ff6f6d0 100644
--- a/wrapper/src/test/java/integration/TestEnvironmentRequest.java
+++ b/wrapper/src/test/java/integration/TestEnvironmentRequest.java
@@ -19,8 +19,7 @@
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
-import java.util.HashSet;
-import java.util.Set;
+import java.util.EnumSet;
@JsonIgnoreProperties(ignoreUnknown = true)
public class TestEnvironmentRequest {
@@ -41,7 +40,7 @@ public class TestEnvironmentRequest {
private TargetJvm targetJvm;
@JsonProperty("features")
- private final Set features = new HashSet<>();
+ private final EnumSet features = EnumSet.noneOf(TestEnvironmentFeatures.class);
@JsonProperty("numOfInstances")
private int numOfInstances = 1;
@@ -93,7 +92,7 @@ public TargetJvm getTargetJvm() {
}
@JsonIgnore
- public Set getFeatures() {
+ public EnumSet getFeatures() {
return this.features;
}
diff --git a/wrapper/src/test/java/integration/container/ConnectionStringHelper.java b/wrapper/src/test/java/integration/container/ConnectionStringHelper.java
index ba3ab227f..7e8b2dfc8 100644
--- a/wrapper/src/test/java/integration/container/ConnectionStringHelper.java
+++ b/wrapper/src/test/java/integration/container/ConnectionStringHelper.java
@@ -21,8 +21,8 @@
import integration.TestEnvironmentFeatures;
import integration.TestEnvironmentInfo;
import integration.TestInstanceInfo;
+import java.util.EnumSet;
import java.util.Properties;
-import java.util.Set;
import software.amazon.jdbc.PropertyDefinition;
import software.amazon.jdbc.util.StringUtils;
@@ -196,7 +196,7 @@ public static Properties getDefaultProperties() {
props.setProperty(PropertyDefinition.USER.name, envInfo.getDatabaseInfo().getUsername());
props.setProperty(PropertyDefinition.PASSWORD.name, envInfo.getDatabaseInfo().getPassword());
- final Set features = envInfo.getRequest().getFeatures();
+ final EnumSet features = envInfo.getRequest().getFeatures();
props.setProperty(PropertyDefinition.ENABLE_TELEMETRY.name, "true");
props.setProperty(PropertyDefinition.TELEMETRY_SUBMIT_TOPLEVEL.name, "true");
props.setProperty(
diff --git a/wrapper/src/test/java/integration/container/TestDriverProvider.java b/wrapper/src/test/java/integration/container/TestDriverProvider.java
index 817d0a1b0..7a7d0df1d 100644
--- a/wrapper/src/test/java/integration/container/TestDriverProvider.java
+++ b/wrapper/src/test/java/integration/container/TestDriverProvider.java
@@ -39,8 +39,8 @@
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.EnumSet;
import java.util.List;
-import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.logging.Logger;
import java.util.stream.Collectors;
@@ -187,7 +187,7 @@ public void beforeEach(ExtensionContext context) throws Exception {
@Override
public void afterEach(ExtensionContext context) throws Exception {
- Set features = TestEnvironment.getCurrent()
+ EnumSet features = TestEnvironment.getCurrent()
.getInfo()
.getRequest()
.getFeatures();
diff --git a/wrapper/src/test/java/integration/container/TestEnvironment.java b/wrapper/src/test/java/integration/container/TestEnvironment.java
index a31eed239..51ba07fdb 100644
--- a/wrapper/src/test/java/integration/container/TestEnvironment.java
+++ b/wrapper/src/test/java/integration/container/TestEnvironment.java
@@ -37,9 +37,9 @@
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
+import java.util.EnumSet;
import java.util.HashMap;
import java.util.List;
-import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.logging.Logger;
import software.amazon.jdbc.Driver;
@@ -249,7 +249,7 @@ public boolean isTestDriverAllowed(TestDriver testDriver) {
boolean disabledByFeature;
boolean driverCompatibleToDatabaseEngine;
- final Set features = this.info.getRequest().getFeatures();
+ final EnumSet features = this.info.getRequest().getFeatures();
final DatabaseEngine databaseEngine = this.info.getRequest().getDatabaseEngine();
switch (testDriver) {
diff --git a/wrapper/src/test/java/integration/container/condition/DisableOnTestFeatureCondition.java b/wrapper/src/test/java/integration/container/condition/DisableOnTestFeatureCondition.java
index 841383cae..c38ecde02 100644
--- a/wrapper/src/test/java/integration/container/condition/DisableOnTestFeatureCondition.java
+++ b/wrapper/src/test/java/integration/container/condition/DisableOnTestFeatureCondition.java
@@ -21,7 +21,7 @@
import integration.TestEnvironmentFeatures;
import integration.container.TestEnvironment;
import java.util.Arrays;
-import java.util.Set;
+import java.util.EnumSet;
import org.junit.jupiter.api.extension.ConditionEvaluationResult;
import org.junit.jupiter.api.extension.ExecutionCondition;
import org.junit.jupiter.api.extension.ExtensionContext;
@@ -33,7 +33,7 @@ public DisableOnTestFeatureCondition() {}
@Override
public ConditionEvaluationResult evaluateExecutionCondition(ExtensionContext context) {
- Set features =
+ EnumSet features =
TestEnvironment.getCurrent().getInfo().getRequest().getFeatures();
boolean disabled =
diff --git a/wrapper/src/test/java/integration/container/condition/EnableOnTestFeatureCondition.java b/wrapper/src/test/java/integration/container/condition/EnableOnTestFeatureCondition.java
index 673a3379e..6c88c60ca 100644
--- a/wrapper/src/test/java/integration/container/condition/EnableOnTestFeatureCondition.java
+++ b/wrapper/src/test/java/integration/container/condition/EnableOnTestFeatureCondition.java
@@ -21,7 +21,7 @@
import integration.TestEnvironmentFeatures;
import integration.container.TestEnvironment;
import java.util.Arrays;
-import java.util.Set;
+import java.util.EnumSet;
import java.util.logging.Logger;
import org.junit.jupiter.api.extension.ConditionEvaluationResult;
import org.junit.jupiter.api.extension.ExecutionCondition;
@@ -37,7 +37,7 @@ public EnableOnTestFeatureCondition() {}
@Override
public ConditionEvaluationResult evaluateExecutionCondition(ExtensionContext context) {
- final Set features =
+ final EnumSet features =
TestEnvironment.getCurrent().getInfo().getRequest().getFeatures();
boolean enabled =
diff --git a/wrapper/src/test/java/software/amazon/jdbc/util/monitoring/MonitorServiceImplTest.java b/wrapper/src/test/java/software/amazon/jdbc/util/monitoring/MonitorServiceImplTest.java
index 895616a26..debb3f243 100644
--- a/wrapper/src/test/java/software/amazon/jdbc/util/monitoring/MonitorServiceImplTest.java
+++ b/wrapper/src/test/java/software/amazon/jdbc/util/monitoring/MonitorServiceImplTest.java
@@ -28,8 +28,7 @@
import static org.mockito.Mockito.spy;
import java.sql.SQLException;
-import java.util.Collections;
-import java.util.HashSet;
+import java.util.EnumSet;
import java.util.Properties;
import java.util.concurrent.TimeUnit;
import org.junit.jupiter.api.AfterEach;
@@ -86,7 +85,7 @@ public void testMonitorError_monitorReCreated() throws SQLException, Interrupted
NoOpMonitor.class,
TimeUnit.MINUTES.toNanos(1),
TimeUnit.MINUTES.toNanos(1),
- new HashSet<>(Collections.singletonList(MonitorErrorResponse.RECREATE)),
+ EnumSet.of(MonitorErrorResponse.RECREATE),
null
);
String key = "testMonitor";
@@ -131,7 +130,7 @@ public void testMonitorStuck_monitorReCreated() throws SQLException, Interrupted
NoOpMonitor.class,
TimeUnit.MINUTES.toNanos(1),
1, // heartbeat times out immediately
- new HashSet<>(Collections.singletonList(MonitorErrorResponse.RECREATE)),
+ EnumSet.of(MonitorErrorResponse.RECREATE),
null
);
String key = "testMonitor";
@@ -178,7 +177,7 @@ public void testMonitorExpired() throws SQLException, InterruptedException {
TimeUnit.MINUTES.toNanos(1),
// even though we pass a re-create policy, we should not re-create it if the monitor is expired since this
// indicates it is not being used.
- new HashSet<>(Collections.singletonList(MonitorErrorResponse.RECREATE)),
+ EnumSet.of(MonitorErrorResponse.RECREATE),
null
);
String key = "testMonitor";
@@ -242,7 +241,7 @@ public void testRemove() throws SQLException, InterruptedException {
TimeUnit.MINUTES.toNanos(1),
// even though we pass a re-create policy, we should not re-create it if the monitor is expired since this
// indicates it is not being used.
- new HashSet<>(Collections.singletonList(MonitorErrorResponse.RECREATE)),
+ EnumSet.of(MonitorErrorResponse.RECREATE),
null
);
@@ -278,7 +277,7 @@ public void testStopAndRemove() throws SQLException, InterruptedException {
TimeUnit.MINUTES.toNanos(1),
// even though we pass a re-create policy, we should not re-create it if the monitor is expired since this
// indicates it is not being used.
- new HashSet<>(Collections.singletonList(MonitorErrorResponse.RECREATE)),
+ EnumSet.of(MonitorErrorResponse.RECREATE),
null
);
From 68360cf32cc25bf78bfb0b57711dccd638a2ee13 Mon Sep 17 00:00:00 2001
From: sergiyvamz <75754709+sergiyvamz@users.noreply.github.com>
Date: Fri, 24 Oct 2025 16:31:34 -0700
Subject: [PATCH 07/38] deprecate enableGreenNodeReplacement parameter (#1578)
Co-authored-by: Karen <64801825+karenc-bq@users.noreply.github.com>
---
docs/using-the-jdbc-driver/UsingTheJdbcDriver.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/using-the-jdbc-driver/UsingTheJdbcDriver.md b/docs/using-the-jdbc-driver/UsingTheJdbcDriver.md
index 063ca8143..2ef0a8b35 100644
--- a/docs/using-the-jdbc-driver/UsingTheJdbcDriver.md
+++ b/docs/using-the-jdbc-driver/UsingTheJdbcDriver.md
@@ -98,7 +98,7 @@ These parameters are applicable to any instance of the AWS Advanced JDBC Wrapper
| `resetSessionStateOnClose` | `Boolean` | No | Enables resetting the session state before closing connection. | `true` |
| `rollbackOnSwitch` | `Boolean` | No | Enables rolling back a current transaction, if any in effect, before switching to a new connection. | `true` |
| `awsProfile` | `String` | No | Allows users to specify a profile name for AWS credentials. This parameter is used by plugins that require AWS credentials, like the [IAM Authentication Connection Plugin](./using-plugins/UsingTheIamAuthenticationPlugin.md) and the [AWS Secrets Manager Connection Plugin](./using-plugins/UsingTheAwsSecretsManagerPlugin.md). | `null` |
-| `enableGreenNodeReplacement` | `Boolean` | No | Enables replacing a green node host name with the original host name when the green host DNS doesn't exist anymore after a blue/green switchover. Refer to [Overview of Amazon RDS Blue/Green Deployments](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/blue-green-deployments-overview.html) for more details about green and blue nodes. | `false` |
+| ~~`enableGreenNodeReplacement`~~ | `Boolean` | No | **Deprecated. Use `bg` plugin instead.** Enables replacing a green node host name with the original host name when the green host DNS doesn't exist anymore after a blue/green switchover. Refer to [Overview of Amazon RDS Blue/Green Deployments](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/blue-green-deployments-overview.html) for more details about green and blue nodes. | `false` |
| `wrapperCaseSensitive`,
`wrappercasesensitive` | `Boolean` | No | Allows the driver to change case sensitivity for parameter names in the connection string and in connection properties. Set parameter to `false` to allow case-insensitive parameter names. | `true` |
| `skipWrappingForPackages` | `String` | No | Register Java package names (separated by comma) which will be left unwrapped. This setting modifies all future connections established by the driver, not just a particular connection. | `com.pgvector` |
From aae443b52bd71d74c9654c475e7a67a9b83f99db Mon Sep 17 00:00:00 2001
From: sergiyvamz <75754709+sergiyvamz@users.noreply.github.com>
Date: Fri, 24 Oct 2025 16:32:04 -0700
Subject: [PATCH 08/38] Remove deprecated configuration parameters (#1577)
---
.../using-plugins/UsingTheFailoverPlugin.md | 2 --
examples/SpringTxFailoverExample/README.md | 4 ----
.../src/main/resources/application.yml | 3 +--
.../test/java/integration/container/tests/FailoverTest.java | 4 ++--
4 files changed, 3 insertions(+), 10 deletions(-)
diff --git a/docs/using-the-jdbc-driver/using-plugins/UsingTheFailoverPlugin.md b/docs/using-the-jdbc-driver/using-plugins/UsingTheFailoverPlugin.md
index aa45662f2..1f72e3ea3 100644
--- a/docs/using-the-jdbc-driver/using-plugins/UsingTheFailoverPlugin.md
+++ b/docs/using-the-jdbc-driver/using-plugins/UsingTheFailoverPlugin.md
@@ -36,8 +36,6 @@ In addition to the parameters that you can configure for the underlying driver,
| `failoverWriterReconnectIntervalMs` | Integer | No | Interval of time in milliseconds to wait between attempts to reconnect to a failed writer during a writer failover process. | `2000` |
| `enableConnectFailover` | Boolean | No | Enables/disables cluster-aware failover if the initial connection to the database fails due to a network exception. Note that this may result in a connection to a different instance in the cluster than was specified by the URL. | `false` |
| `skipFailoverOnInterruptedThread` | Boolean | No | Enable to skip failover if the current thread is interrupted. This may leave the Connection in an invalid state so the Connection should be disposed. | `false` |
-| ~~`keepSessionStateOnFailover`~~ | Boolean | No | This parameter is no longer available. If specified, it will be ignored by the driver. See [Session State](../SessionState.md) for more details. | `false` |
-| ~~`enableFailoverStrictReader`~~ | Boolean | No | This parameter is no longer available and, if specified, it will be ignored by the driver. See `failoverMode` (`reader-or-writer` or `strict-reader`) for more details. | |
## Host Pattern
When connecting to Aurora clusters, the [`clusterInstanceHostPattern`](#failover-parameters) parameter is required if the connection string does not provide enough information about the database cluster domain name. If the Aurora cluster endpoint is used directly, the AWS Advanced JDBC Wrapper will recognize the standard Aurora domain name and can re-build a proper Aurora instance name when needed. In cases where the connection string uses an IP address, a custom domain name, or localhost, the driver won't know how to build a proper domain name for a database instance endpoint. For example, if a custom domain was being used and the cluster instance endpoints followed a pattern of `instanceIdentifier1.customHost`, `instanceIdentifier2.customHost`, etc., the driver would need to know how to construct the instance endpoints using the specified custom domain. Since there isn't enough information from the custom domain alone to create the instance endpoints, you should set the `clusterInstanceHostPattern` to `?.customHost`, making the connection string `jdbc:aws-wrapper:postgresql://customHost:1234/test?clusterInstanceHostPattern=?.customHost`. Refer to [this diagram](../../images/failover_behavior.png) about AWS Advanced JDBC Wrapper behavior during failover for different connection URLs and more details and examples.
diff --git a/examples/SpringTxFailoverExample/README.md b/examples/SpringTxFailoverExample/README.md
index 53364492a..72a55125e 100644
--- a/examples/SpringTxFailoverExample/README.md
+++ b/examples/SpringTxFailoverExample/README.md
@@ -127,12 +127,8 @@ spring:
max-lifetime: 1260000
auto-commit: false
maximum-pool-size: 3
- data-source-properties:
- keepSessionStateOnFailover: true
```
-Please also note the use of the [`keepSessionStateOnFailover`](https://github.com/aws/aws-advanced-jdbc-wrapper/blob/main/docs/using-the-jdbc-driver/using-plugins/UsingTheFailoverPlugin.md#failover-parameters) property. When failover occurs, the connection's auto commit value is reset to true. When the auto commit value is set to false or transactions are used, further operations such as a rollback or commit on the same connection will cause errors. This parameter is used when connections cannot be reconfigured manually as seen in this [example](https://github.com/aws/aws-advanced-jdbc-wrapper/tree/main/examples/AWSDriverExample/src/main/java/software/amazon/PgFailoverSample.java).
-
## Step 4: Set up a data access object
Set up a simple data access object (DAO) interface and implementation. The data access object will be responsible for executing any queries. In this tutorial, only a get method will be included, but other methods are available within the sample code.
diff --git a/examples/SpringTxFailoverExample/src/main/resources/application.yml b/examples/SpringTxFailoverExample/src/main/resources/application.yml
index 31f8417a8..1d46238b4 100644
--- a/examples/SpringTxFailoverExample/src/main/resources/application.yml
+++ b/examples/SpringTxFailoverExample/src/main/resources/application.yml
@@ -9,5 +9,4 @@ spring:
max-lifetime: 1260000
auto-commit: false
maximum-pool-size: 3
- data-source-properties:
- keepSessionStateOnFailover: true
+
diff --git a/wrapper/src/test/java/integration/container/tests/FailoverTest.java b/wrapper/src/test/java/integration/container/tests/FailoverTest.java
index 347264561..758e5ca5f 100644
--- a/wrapper/src/test/java/integration/container/tests/FailoverTest.java
+++ b/wrapper/src/test/java/integration/container/tests/FailoverTest.java
@@ -505,11 +505,11 @@ public void test_takeOverConnectionProperties() throws SQLException {
/**
* Current writer dies, a reader instance is nominated to be a new writer, failover to the new
- * writer. Autocommit is set to false and the keepSessionStateOnFailover property is set to true.
+ * writer. Autocommit is set to false.
*/
@TestTemplate
@EnableOnNumOfInstances(min = 2)
- public void test_failFromWriterWhereKeepSessionStateOnFailoverIsTrue() throws SQLException {
+ public void test_failFromWriter() throws SQLException {
final String initialWriterId = this.currentWriter;
TestInstanceInfo initialWriterInstanceInfo =
From 5b5097538156eec4721573a260b9b8d4fab61f55 Mon Sep 17 00:00:00 2001
From: sergiyvamz <75754709+sergiyvamz@users.noreply.github.com>
Date: Wed, 29 Oct 2025 17:28:49 -0700
Subject: [PATCH 09/38] fix wrong nodeId in HostSpec (#1579)
---
.../java/software/amazon/jdbc/HostSpec.java | 4 +--
.../jdbc/dialect/AuroraMysqlDialect.java | 2 +-
.../amazon/jdbc/dialect/AuroraPgDialect.java | 3 +-
.../RdsMultiAzDbClusterMysqlDialect.java | 6 +++-
.../dialect/RdsMultiAzDbClusterPgDialect.java | 6 +++-
.../ClusterTopologyMonitorImpl.java | 28 ++++++++++++++-----
.../GlobalDbClusterTopologyMonitorImpl.java | 3 +-
.../MultiAzClusterTopologyMonitorImpl.java | 2 +-
8 files changed, 39 insertions(+), 15 deletions(-)
diff --git a/wrapper/src/main/java/software/amazon/jdbc/HostSpec.java b/wrapper/src/main/java/software/amazon/jdbc/HostSpec.java
index 3b6f64ca1..5c3babb53 100644
--- a/wrapper/src/main/java/software/amazon/jdbc/HostSpec.java
+++ b/wrapper/src/main/java/software/amazon/jdbc/HostSpec.java
@@ -205,9 +205,9 @@ public Set asAliases() {
}
public String toString() {
- return String.format("HostSpec@%s [host=%s, port=%d, %s, %s, weight=%d, %s]",
+ return String.format("HostSpec@%s [hostId=%s, host=%s, port=%d, %s, %s, weight=%d, %s]",
Integer.toHexString(System.identityHashCode(this)),
- this.host, this.port, this.role, this.availability, this.weight, this.lastUpdateTime);
+ this.hostId, this.host, this.port, this.role, this.availability, this.weight, this.lastUpdateTime);
}
@Override
diff --git a/wrapper/src/main/java/software/amazon/jdbc/dialect/AuroraMysqlDialect.java b/wrapper/src/main/java/software/amazon/jdbc/dialect/AuroraMysqlDialect.java
index 2f5a8c91c..1e44afdb0 100644
--- a/wrapper/src/main/java/software/amazon/jdbc/dialect/AuroraMysqlDialect.java
+++ b/wrapper/src/main/java/software/amazon/jdbc/dialect/AuroraMysqlDialect.java
@@ -41,7 +41,7 @@ public class AuroraMysqlDialect extends MysqlDialect implements BlueGreenDialect
"SELECT SERVER_ID FROM information_schema.replica_host_status "
+ "WHERE SESSION_ID = 'MASTER_SESSION_ID' AND SERVER_ID = @@aurora_server_id";
- protected final String nodeIdQuery = "SELECT @@aurora_server_id";
+ protected final String nodeIdQuery = "SELECT @@aurora_server_id, @@aurora_server_id";
protected final String isReaderQuery = "SELECT @@innodb_read_only";
private static final String BG_STATUS_QUERY =
diff --git a/wrapper/src/main/java/software/amazon/jdbc/dialect/AuroraPgDialect.java b/wrapper/src/main/java/software/amazon/jdbc/dialect/AuroraPgDialect.java
index a12046ea4..c88f595af 100644
--- a/wrapper/src/main/java/software/amazon/jdbc/dialect/AuroraPgDialect.java
+++ b/wrapper/src/main/java/software/amazon/jdbc/dialect/AuroraPgDialect.java
@@ -58,7 +58,8 @@ public class AuroraPgDialect extends PgDialect implements AuroraLimitlessDialect
+ "WHERE SESSION_ID OPERATOR(pg_catalog.=) 'MASTER_SESSION_ID' "
+ "AND SERVER_ID OPERATOR(pg_catalog.=) pg_catalog.aurora_db_instance_identifier()";
- protected final String nodeIdQuery = "SELECT pg_catalog.aurora_db_instance_identifier()";
+ protected final String nodeIdQuery =
+ "SELECT pg_catalog.aurora_db_instance_identifier(), pg_catalog.aurora_db_instance_identifier()";
protected final String isReaderQuery = "SELECT pg_catalog.pg_is_in_recovery()";
protected static final String LIMITLESS_ROUTER_ENDPOINT_QUERY =
"select router_endpoint, load from pg_catalog.aurora_limitless_router_endpoints()";
diff --git a/wrapper/src/main/java/software/amazon/jdbc/dialect/RdsMultiAzDbClusterMysqlDialect.java b/wrapper/src/main/java/software/amazon/jdbc/dialect/RdsMultiAzDbClusterMysqlDialect.java
index 930cf1631..9c920c8e6 100644
--- a/wrapper/src/main/java/software/amazon/jdbc/dialect/RdsMultiAzDbClusterMysqlDialect.java
+++ b/wrapper/src/main/java/software/amazon/jdbc/dialect/RdsMultiAzDbClusterMysqlDialect.java
@@ -47,7 +47,11 @@ public class RdsMultiAzDbClusterMysqlDialect extends MysqlDialect {
private static final String FETCH_WRITER_NODE_QUERY_COLUMN_NAME = "Source_Server_Id";
- private static final String NODE_ID_QUERY = "SELECT @@server_id";
+ // The query return nodeId and nodeName.
+ // For example: "1845128080", "test-multiaz-instance-1"
+ private static final String NODE_ID_QUERY = "SELECT id, SUBSTRING_INDEX(endpoint, '.', 1)"
+ + " FROM mysql.rds_topology"
+ + " WHERE id = @@server_id";
private static final String IS_READER_QUERY = "SELECT @@read_only";
private static final EnumSet RDS_MULTI_AZ_RESTRICTIONS =
diff --git a/wrapper/src/main/java/software/amazon/jdbc/dialect/RdsMultiAzDbClusterPgDialect.java b/wrapper/src/main/java/software/amazon/jdbc/dialect/RdsMultiAzDbClusterPgDialect.java
index eb3796adb..0f6f51301 100644
--- a/wrapper/src/main/java/software/amazon/jdbc/dialect/RdsMultiAzDbClusterPgDialect.java
+++ b/wrapper/src/main/java/software/amazon/jdbc/dialect/RdsMultiAzDbClusterPgDialect.java
@@ -50,7 +50,11 @@ public class RdsMultiAzDbClusterPgDialect extends PgDialect {
private static final String FETCH_WRITER_NODE_QUERY_COLUMN_NAME = "multi_az_db_cluster_source_dbi_resource_id";
- private static final String NODE_ID_QUERY = "SELECT dbi_resource_id FROM rds_tools.dbi_resource_id()";
+ // The query return nodeId and nodeName.
+ // For example: "db-WQFQKBTL2LQUPIEFIFBGENS4ZQ", "test-multiaz-instance-1"
+ private static final String NODE_ID_QUERY = "SELECT id, SUBSTRING(endpoint FROM 0 FOR POSITION('.' IN endpoint))"
+ + " FROM rds_tools.show_topology()"
+ + " WHERE id OPERATOR(pg_catalog.=) rds_tools.dbi_resource_id()";
private static final String IS_READER_QUERY = "SELECT pg_catalog.pg_is_in_recovery()";
diff --git a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/ClusterTopologyMonitorImpl.java b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/ClusterTopologyMonitorImpl.java
index 84f41ff02..d7d2f7e62 100644
--- a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/ClusterTopologyMonitorImpl.java
+++ b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/ClusterTopologyMonitorImpl.java
@@ -51,6 +51,7 @@
import software.amazon.jdbc.util.ExecutorFactory;
import software.amazon.jdbc.util.FullServicesContainer;
import software.amazon.jdbc.util.Messages;
+import software.amazon.jdbc.util.Pair;
import software.amazon.jdbc.util.PropertyUtils;
import software.amazon.jdbc.util.RdsUtils;
import software.amazon.jdbc.util.ServiceUtility;
@@ -582,10 +583,10 @@ protected List openAnyConnectionAndUpdateTopology() {
"ClusterTopologyMonitorImpl.writerMonitoringConnection",
new Object[]{this.writerHostSpec.get().getHost()}));
} else {
- final String nodeId = this.getNodeId(this.monitoringConnection.get());
- if (!StringUtils.isNullOrEmpty(nodeId)) {
- this.writerHostSpec.set(this.createHost(nodeId, true, 0, null,
- this.getClusterInstanceTemplate(nodeId, this.monitoringConnection.get())));
+ final Pair pair = this.getNodeId(this.monitoringConnection.get());
+ if (pair != null) {
+ this.writerHostSpec.set(this.createHost(pair.getValue1(), pair.getValue2(), true, 0, null,
+ this.getClusterInstanceTemplate(pair.getValue2(), this.monitoringConnection.get())));
LOGGER.finest(() -> Messages.get(
"ClusterTopologyMonitorImpl.writerMonitoringConnection",
new Object[]{this.writerHostSpec.get().getHost()}));
@@ -627,12 +628,23 @@ protected HostSpec getClusterInstanceTemplate(String nodeId, Connection connecti
return this.clusterInstanceTemplate;
}
- protected String getNodeId(final Connection connection) {
+ /**
+ * Identifies nodes across different database types using nodeId and nodeName values.
+ *
+ * Database types handle these identifiers differently:
+ * - Aurora: Uses the instance (node) name as both nodeId and nodeName
+ * Example: "test-instance-1" for both values
+ * - RDS Cluster: Uses distinct values for nodeId and nodeName
+ * Example:
+ * nodeId: "db-WQFQKBTL2LQUPIEFIFBGENS4ZQ"
+ * nodeName: "test-multiaz-instance-1"
+ */
+ protected Pair getNodeId(final Connection connection) {
try {
try (final Statement stmt = connection.createStatement();
final ResultSet resultSet = stmt.executeQuery(this.nodeIdQuery)) {
if (resultSet.next()) {
- return resultSet.getString(1);
+ return Pair.create(resultSet.getString(1), resultSet.getString(2));
}
}
} catch (SQLException ex) {
@@ -840,10 +852,11 @@ protected HostSpec createHost(
// Calculate weight based on node lag in time and CPU utilization.
final long weight = Math.round(nodeLag) * 100L + Math.round(cpuUtilization);
- return createHost(hostName, isWriter, weight, lastUpdateTime, this.clusterInstanceTemplate);
+ return createHost(hostName, hostName, isWriter, weight, lastUpdateTime, this.clusterInstanceTemplate);
}
protected HostSpec createHost(
+ String nodeId,
String nodeName,
final boolean isWriter,
final long weight,
@@ -857,6 +870,7 @@ protected HostSpec createHost(
: this.initialHostSpec.getPort();
final HostSpec hostSpec = this.servicesContainer.getHostListProviderService().getHostSpecBuilder()
+ .hostId(nodeId)
.host(endpoint)
.port(port)
.role(isWriter ? HostRole.WRITER : HostRole.READER)
diff --git a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/GlobalDbClusterTopologyMonitorImpl.java b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/GlobalDbClusterTopologyMonitorImpl.java
index 4b661dbc5..0c2ee29a2 100644
--- a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/GlobalDbClusterTopologyMonitorImpl.java
+++ b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/GlobalDbClusterTopologyMonitorImpl.java
@@ -105,6 +105,7 @@ protected HostSpec createHost(
throw new SQLException("Can't find cluster template for region " + awsRegion);
}
- return createHost(hostName, isWriter, weight, Timestamp.from(Instant.now()), clusterInstanceTemplateForRegion);
+ return createHost(
+ hostName, hostName, isWriter, weight, Timestamp.from(Instant.now()), clusterInstanceTemplateForRegion);
}
}
diff --git a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MultiAzClusterTopologyMonitorImpl.java b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MultiAzClusterTopologyMonitorImpl.java
index 7c744b0d5..de23fb34c 100644
--- a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MultiAzClusterTopologyMonitorImpl.java
+++ b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MultiAzClusterTopologyMonitorImpl.java
@@ -123,6 +123,6 @@ protected HostSpec createHost(
String hostId = resultSet.getString("id"); // "1034958454"
final boolean isWriter = hostId.equals(suggestedWriterNodeId);
- return createHost(instanceName, isWriter, 0, Timestamp.from(Instant.now()), this.clusterInstanceTemplate);
+ return createHost(hostId, instanceName, isWriter, 0, Timestamp.from(Instant.now()), this.clusterInstanceTemplate);
}
}
From dc69306c9228a4a364c861c8c97dc93fb1cb9ea8 Mon Sep 17 00:00:00 2001
From: sergiyvamz <75754709+sergiyvamz@users.noreply.github.com>
Date: Wed, 29 Oct 2025 18:02:08 -0700
Subject: [PATCH 10/38] feat: add support of RDS Proxy custom endpoints (#1583)
---
.../hostlistprovider/RdsHostListProvider.java | 2 +-
.../failover/FailoverConnectionPlugin.java | 1 +
.../failover2/FailoverConnectionPlugin.java | 1 +
.../software/amazon/jdbc/util/RdsUrlType.java | 1 +
.../software/amazon/jdbc/util/RdsUtils.java | 55 +++++++++++++++++++
.../amazon/jdbc/util/RdsUtilsTests.java | 14 +++++
6 files changed, 73 insertions(+), 1 deletion(-)
diff --git a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/RdsHostListProvider.java b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/RdsHostListProvider.java
index cc17096e5..aed5f83d7 100644
--- a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/RdsHostListProvider.java
+++ b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/RdsHostListProvider.java
@@ -447,7 +447,7 @@ protected void validateHostPatternSetting(final String hostPattern) {
}
final RdsUrlType rdsUrlType = rdsHelper.identifyRdsType(hostPattern);
- if (rdsUrlType == RdsUrlType.RDS_PROXY) {
+ if (rdsUrlType == RdsUrlType.RDS_PROXY || rdsUrlType == RdsUrlType.RDS_PROXY_ENDPOINT) {
// "An RDS Proxy url can't be used as the 'clusterInstanceHostPattern' configuration setting."
final String message =
Messages.get("RdsHostListProvider.clusterInstanceHostPatternNotSupportedForRDSProxy");
diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/failover/FailoverConnectionPlugin.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/failover/FailoverConnectionPlugin.java
index ef2f95550..25ce77ecb 100644
--- a/wrapper/src/main/java/software/amazon/jdbc/plugin/failover/FailoverConnectionPlugin.java
+++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/failover/FailoverConnectionPlugin.java
@@ -388,6 +388,7 @@ private boolean isNodeStillValid(final String node, final Map.global-.global.rds.amazonaws.com
+ // Example: test-global-db-name.global-123456789012.global.rds.amazonaws.com
+ //
+ //
+ // RDS Proxy
+ // RDS Proxy Endpoint: .proxy-..rds.amazonaws.com
+ // Example: test-rds-proxy-name.proxy-123456789012.us-east-2.rds.amazonaws.com
+ //
+ // RDS Proxy Custom Endpoint: .endpoint.proxy-..rds.amazonaws.com
+ // Example: test-custom-endpoint-name.endpoint.proxy-123456789012.us-east-2.rds.amazonaws.com
private static final Pattern AURORA_DNS_PATTERN =
Pattern.compile(
@@ -185,6 +199,30 @@ public class RdsUtils {
+ "(?[a-zA-Z0-9]+\\.global\\.rds\\.amazonaws\\.com\\.?)$",
Pattern.CASE_INSENSITIVE);
+ private static final Pattern RDS_PROXY_ENDPOINT_DNS_PATTERN =
+ Pattern.compile(
+ "^(?.+)\\.endpoint\\."
+ + "(?proxy-)?"
+ + "(?[a-zA-Z0-9]+\\.(?[a-zA-Z0-9\\-]+)"
+ + "\\.rds\\.amazonaws\\.com\\.?)$",
+ Pattern.CASE_INSENSITIVE);
+
+ private static final Pattern RDS_PROXY_ENDPOINT_CHINA_DNS_PATTERN =
+ Pattern.compile(
+ "^(?.+)\\.endpoint\\."
+ + "(?proxy-)+"
+ + "(?[a-zA-Z0-9]+\\.rds\\.(?[a-zA-Z0-9\\-]+)"
+ + "\\.amazonaws\\.com\\.cn\\.?)$",
+ Pattern.CASE_INSENSITIVE);
+
+ private static final Pattern RDS_PROXY_ENDPOINT_OLD_CHINA_DNS_PATTERN =
+ Pattern.compile(
+ "^(?.+)\\.endpoint\\."
+ + "(?proxy-)?"
+ + "(?[a-zA-Z0-9]+\\.(?[a-zA-Z0-9\\-]+)"
+ + "\\.rds\\.amazonaws\\.com\\.cn\\.?)$",
+ Pattern.CASE_INSENSITIVE);
+
private static final Map cachedPatterns = new ConcurrentHashMap<>();
private static final Map cachedDnsPatterns = new ConcurrentHashMap<>();
@@ -226,6 +264,21 @@ public boolean isRdsProxyDns(final String host) {
return dnsGroup != null && dnsGroup.startsWith("proxy-");
}
+ public boolean isRdsProxyEndpointDns(final String host) {
+ final String preparedHost = getPreparedHost(host);
+ if (StringUtils.isNullOrEmpty(preparedHost)) {
+ return false;
+ }
+
+ final Matcher matcher = cacheMatcher(preparedHost,
+ RDS_PROXY_ENDPOINT_DNS_PATTERN, RDS_PROXY_ENDPOINT_CHINA_DNS_PATTERN, RDS_PROXY_ENDPOINT_OLD_CHINA_DNS_PATTERN);
+ if (getRegexGroup(matcher, DNS_GROUP) != null) {
+ return getRegexGroup(matcher, INSTANCE_GROUP) != null;
+ }
+
+ return false;
+ }
+
public @Nullable String getRdsClusterId(final String host) {
final String preparedHost = getPreparedHost(host);
if (StringUtils.isNullOrEmpty(preparedHost)) {
@@ -373,6 +426,8 @@ public RdsUrlType identifyRdsType(final String host) {
return RdsUrlType.RDS_AURORA_LIMITLESS_DB_SHARD_GROUP;
} else if (isRdsProxyDns(host)) {
return RdsUrlType.RDS_PROXY;
+ } else if (isRdsProxyEndpointDns(host)) {
+ return RdsUrlType.RDS_PROXY_ENDPOINT;
} else if (isRdsDns(host)) {
return RdsUrlType.RDS_INSTANCE;
} else {
diff --git a/wrapper/src/test/java/software/amazon/jdbc/util/RdsUtilsTests.java b/wrapper/src/test/java/software/amazon/jdbc/util/RdsUtilsTests.java
index 1001575b5..a0b5090ab 100644
--- a/wrapper/src/test/java/software/amazon/jdbc/util/RdsUtilsTests.java
+++ b/wrapper/src/test/java/software/amazon/jdbc/util/RdsUtilsTests.java
@@ -37,6 +37,8 @@ public class RdsUtilsTests {
"instance-test-name.XYZ.us-east-2.rds.amazonaws.com";
private static final String usEastRegionProxy =
"proxy-test-name.proxy-XYZ.us-east-2.rds.amazonaws.com";
+ private static final String usEastRegionProxyEndpoint =
+ "endpoint-test-name.endpoint.proxy-XYZ.us-east-2.rds.amazonaws.com";
private static final String usEastRegionCustomDomain =
"custom-test-name.cluster-custom-XYZ.us-east-2.rds.amazonaws.com";
private static final String usEastRegionLimitlessDbShardGroup =
@@ -135,6 +137,7 @@ public void testIsRdsDns() {
assertTrue(target.isRdsDns(usEastRegionClusterReadOnly));
assertTrue(target.isRdsDns(usEastRegionInstance));
assertTrue(target.isRdsDns(usEastRegionProxy));
+ assertTrue(target.isRdsDns(usEastRegionProxyEndpoint));
assertTrue(target.isRdsDns(usEastRegionCustomDomain));
assertFalse(target.isRdsDns(usEastRegionElbUrl));
assertFalse(target.isRdsDns(usEastRegionElbUrlTrailingDot));
@@ -228,6 +231,7 @@ public void testIsRdsClusterDns() {
assertTrue(target.isRdsClusterDns(usEastRegionClusterReadOnly));
assertFalse(target.isRdsClusterDns(usEastRegionInstance));
assertFalse(target.isRdsClusterDns(usEastRegionProxy));
+ assertFalse(target.isRdsClusterDns(usEastRegionProxyEndpoint));
assertFalse(target.isRdsClusterDns(usEastRegionCustomDomain));
assertFalse(target.isRdsClusterDns(usEastRegionElbUrl));
assertFalse(target.isRdsClusterDns(usEastRegionLimitlessDbShardGroup));
@@ -268,6 +272,7 @@ public void testIsWriterClusterDns() {
assertFalse(target.isWriterClusterDns(usEastRegionClusterReadOnly));
assertFalse(target.isWriterClusterDns(usEastRegionInstance));
assertFalse(target.isWriterClusterDns(usEastRegionProxy));
+ assertFalse(target.isWriterClusterDns(usEastRegionProxyEndpoint));
assertFalse(target.isWriterClusterDns(usEastRegionCustomDomain));
assertFalse(target.isWriterClusterDns(usEastRegionElbUrl));
assertFalse(target.isWriterClusterDns(usEastRegionLimitlessDbShardGroup));
@@ -308,6 +313,7 @@ public void testIsReaderClusterDns() {
assertTrue(target.isReaderClusterDns(usEastRegionClusterReadOnly));
assertFalse(target.isReaderClusterDns(usEastRegionInstance));
assertFalse(target.isReaderClusterDns(usEastRegionProxy));
+ assertFalse(target.isReaderClusterDns(usEastRegionProxyEndpoint));
assertFalse(target.isReaderClusterDns(usEastRegionCustomDomain));
assertFalse(target.isReaderClusterDns(usEastRegionElbUrl));
assertFalse(target.isReaderClusterDns(usEastRegionLimitlessDbShardGroup));
@@ -348,6 +354,7 @@ public void testIsLimitlessDbShardGroupDns() {
assertFalse(target.isLimitlessDbShardGroupDns(usEastRegionClusterReadOnly));
assertFalse(target.isLimitlessDbShardGroupDns(usEastRegionInstance));
assertFalse(target.isLimitlessDbShardGroupDns(usEastRegionProxy));
+ assertFalse(target.isLimitlessDbShardGroupDns(usEastRegionProxyEndpoint));
assertFalse(target.isLimitlessDbShardGroupDns(usEastRegionCustomDomain));
assertFalse(target.isLimitlessDbShardGroupDns(usEastRegionElbUrl));
assertTrue(target.isLimitlessDbShardGroupDns(usEastRegionLimitlessDbShardGroup));
@@ -389,6 +396,7 @@ public void testGetRdsRegion() {
assertEquals(expectedHostPattern, target.getRdsRegion(usEastRegionClusterReadOnly));
assertEquals(expectedHostPattern, target.getRdsRegion(usEastRegionInstance));
assertEquals(expectedHostPattern, target.getRdsRegion(usEastRegionProxy));
+ assertEquals(expectedHostPattern, target.getRdsRegion(usEastRegionProxyEndpoint));
assertEquals(expectedHostPattern, target.getRdsRegion(usEastRegionCustomDomain));
assertEquals(expectedHostPattern, target.getRdsRegion(usEastRegionElbUrl));
assertEquals(expectedHostPattern, target.getRdsRegion(usEastRegionLimitlessDbShardGroup));
@@ -437,6 +445,12 @@ public void testIsGlobalDbWriterClusterDns() {
assertTrue(target.isGlobalDbWriterClusterDns(globalDbWriterCluster));
}
+ @Test
+ public void testisRdsProxyEndpointDns() {
+ assertFalse(target.isRdsProxyEndpointDns(usEastRegionProxy));
+ assertTrue(target.isRdsProxyEndpointDns(usEastRegionProxyEndpoint));
+ }
+
@Test
public void testBrokenPathsHostPattern() {
final String incorrectChinaHostPattern = "?.rds.cn-northwest-1.rds.amazonaws.com.cn";
From 4538b12de6573c238a8fe002c12d127bfffbd104 Mon Sep 17 00:00:00 2001
From: sergiyvamz <75754709+sergiyvamz@users.noreply.github.com>
Date: Thu, 30 Oct 2025 11:57:16 -0700
Subject: [PATCH 11/38] feat: pooled flag for connect (#1581)
---
.../ConnectionPluginManagerBenchmarks.java | 3 +-
.../jdbc/benchmarks/PluginBenchmarks.java | 3 +-
.../jdbc/C3P0PooledConnectionProvider.java | 4 +-
.../software/amazon/jdbc/ConnectionInfo.java | 41 +++++++++++++++++++
.../amazon/jdbc/ConnectionProvider.java | 4 +-
.../jdbc/DataSourceConnectionProvider.java | 6 +--
.../amazon/jdbc/DriverConnectionProvider.java | 6 +--
.../jdbc/HikariPooledConnectionProvider.java | 4 +-
.../amazon/jdbc/PartialPluginService.java | 21 ++++++++++
.../amazon/jdbc/PluginManagerService.java | 6 +++
.../software/amazon/jdbc/PluginService.java | 16 ++++++++
.../amazon/jdbc/PluginServiceImpl.java | 19 +++++++++
.../jdbc/plugin/DefaultConnectionPlugin.java | 16 +++++---
.../ReadWriteSplittingPlugin.java | 4 +-
.../amazon/jdbc/util/WrapperUtils.java | 4 ++
.../jdbc/wrapper/ConnectionWrapper.java | 6 +++
.../jdbc/ConnectionPluginManagerTests.java | 11 ++++-
.../HikariPooledConnectionProviderTest.java | 30 +++++++++++++-
.../plugin/DefaultConnectionPluginTest.java | 5 ++-
.../dev/DeveloperConnectionPluginTest.java | 4 +-
.../ReadWriteSplittingPluginTest.java | 4 +-
.../amazon/jdbc/util/WrapperUtilsTest.java | 9 ++++
22 files changed, 197 insertions(+), 29 deletions(-)
create mode 100644 wrapper/src/main/java/software/amazon/jdbc/ConnectionInfo.java
diff --git a/benchmarks/src/jmh/java/software/amazon/jdbc/benchmarks/ConnectionPluginManagerBenchmarks.java b/benchmarks/src/jmh/java/software/amazon/jdbc/benchmarks/ConnectionPluginManagerBenchmarks.java
index 413a37e03..eb4a8d3b2 100644
--- a/benchmarks/src/jmh/java/software/amazon/jdbc/benchmarks/ConnectionPluginManagerBenchmarks.java
+++ b/benchmarks/src/jmh/java/software/amazon/jdbc/benchmarks/ConnectionPluginManagerBenchmarks.java
@@ -50,6 +50,7 @@
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;
+import software.amazon.jdbc.ConnectionInfo;
import software.amazon.jdbc.ConnectionPluginFactory;
import software.amazon.jdbc.ConnectionPluginManager;
import software.amazon.jdbc.ConnectionProvider;
@@ -126,7 +127,7 @@ public void setUpIteration() throws Exception {
any(Dialect.class),
any(TargetDriverDialect.class),
any(HostSpec.class),
- any(Properties.class))).thenReturn(mockConnection);
+ any(Properties.class))).thenReturn(new ConnectionInfo(mockConnection, false));
when(mockTelemetryFactory.openTelemetryContext(anyString(), any())).thenReturn(mockTelemetryContext);
when(mockTelemetryFactory.openTelemetryContext(eq(null), any())).thenReturn(mockTelemetryContext);
when(mockTelemetryFactory.createCounter(anyString())).thenReturn(mockTelemetryCounter);
diff --git a/benchmarks/src/jmh/java/software/amazon/jdbc/benchmarks/PluginBenchmarks.java b/benchmarks/src/jmh/java/software/amazon/jdbc/benchmarks/PluginBenchmarks.java
index a9c10b2f6..405b6fc00 100644
--- a/benchmarks/src/jmh/java/software/amazon/jdbc/benchmarks/PluginBenchmarks.java
+++ b/benchmarks/src/jmh/java/software/amazon/jdbc/benchmarks/PluginBenchmarks.java
@@ -48,6 +48,7 @@
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;
+import software.amazon.jdbc.ConnectionInfo;
import software.amazon.jdbc.ConnectionPluginManager;
import software.amazon.jdbc.ConnectionProvider;
import software.amazon.jdbc.ConnectionProviderManager;
@@ -134,7 +135,7 @@ public void setUpIteration() throws Exception {
any(Dialect.class),
any(TargetDriverDialect.class),
any(HostSpec.class),
- any(Properties.class))).thenReturn(mockConnection);
+ any(Properties.class))).thenReturn(new ConnectionInfo(mockConnection, false));
when(mockConnection.createStatement()).thenReturn(mockStatement);
when(mockStatement.executeQuery(anyString())).thenReturn(mockResultSet);
when(mockResultSet.next()).thenReturn(true, true, false);
diff --git a/wrapper/src/main/java/software/amazon/jdbc/C3P0PooledConnectionProvider.java b/wrapper/src/main/java/software/amazon/jdbc/C3P0PooledConnectionProvider.java
index dc4aa70eb..0a2fb5841 100644
--- a/wrapper/src/main/java/software/amazon/jdbc/C3P0PooledConnectionProvider.java
+++ b/wrapper/src/main/java/software/amazon/jdbc/C3P0PooledConnectionProvider.java
@@ -79,7 +79,7 @@ public HostSpec getHostSpecByStrategy(@NonNull List hosts, @NonNull Ho
}
@Override
- public Connection connect(@NonNull String protocol, @NonNull Dialect dialect,
+ public @NonNull ConnectionInfo connect(@NonNull String protocol, @NonNull Dialect dialect,
@NonNull TargetDriverDialect targetDriverDialect, @NonNull HostSpec hostSpec,
@NonNull Properties props) throws SQLException {
final Properties copy = PropertyUtils.copyProperties(props);
@@ -93,7 +93,7 @@ public Connection connect(@NonNull String protocol, @NonNull Dialect dialect,
ds.setPassword(copy.getProperty(PropertyDefinition.PASSWORD.name));
- return ds.getConnection();
+ return new ConnectionInfo(ds.getConnection(), true);
}
protected ComboPooledDataSource createDataSource(
diff --git a/wrapper/src/main/java/software/amazon/jdbc/ConnectionInfo.java b/wrapper/src/main/java/software/amazon/jdbc/ConnectionInfo.java
new file mode 100644
index 000000000..ca253e6da
--- /dev/null
+++ b/wrapper/src/main/java/software/amazon/jdbc/ConnectionInfo.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package software.amazon.jdbc;
+
+import java.sql.Connection;
+
+public class ConnectionInfo {
+ private final Connection connection;
+ private final boolean isPooled;
+
+ public ConnectionInfo(Connection connection, boolean isPooled) {
+ this.connection = connection;
+ this.isPooled = isPooled;
+ }
+
+ public ConnectionInfo(Connection connection) {
+ this(connection, false);
+ }
+
+ public Connection getConnection() {
+ return connection;
+ }
+
+ public boolean isPooled() {
+ return isPooled;
+ }
+}
diff --git a/wrapper/src/main/java/software/amazon/jdbc/ConnectionProvider.java b/wrapper/src/main/java/software/amazon/jdbc/ConnectionProvider.java
index 8fddcf269..6967a673c 100644
--- a/wrapper/src/main/java/software/amazon/jdbc/ConnectionProvider.java
+++ b/wrapper/src/main/java/software/amazon/jdbc/ConnectionProvider.java
@@ -78,10 +78,10 @@ HostSpec getHostSpecByStrategy(
* @param targetDriverDialect the target driver dialect
* @param hostSpec the HostSpec containing the host-port information for the host to connect to
* @param props the Properties to use for the connection
- * @return {@link Connection} resulting from the given connection information
+ * @return {@link ConnectionInfo} resulting from the given connection information
* @throws SQLException if an error occurs
*/
- Connection connect(
+ @NonNull ConnectionInfo connect(
@NonNull String protocol,
@NonNull Dialect dialect,
@NonNull TargetDriverDialect targetDriverDialect,
diff --git a/wrapper/src/main/java/software/amazon/jdbc/DataSourceConnectionProvider.java b/wrapper/src/main/java/software/amazon/jdbc/DataSourceConnectionProvider.java
index 6c5afcda3..d12eeefad 100644
--- a/wrapper/src/main/java/software/amazon/jdbc/DataSourceConnectionProvider.java
+++ b/wrapper/src/main/java/software/amazon/jdbc/DataSourceConnectionProvider.java
@@ -110,11 +110,11 @@ public HostSpec getHostSpecByStrategy(
* @param protocol The connection protocol (example "jdbc:mysql://")
* @param hostSpec The HostSpec containing the host-port information for the host to connect to
* @param props The Properties to use for the connection
- * @return {@link Connection} resulting from the given connection information
+ * @return {@link ConnectionInfo} resulting from the given connection information
* @throws SQLException if an error occurs
*/
@Override
- public Connection connect(
+ public @NonNull ConnectionInfo connect(
final @NonNull String protocol,
final @NonNull Dialect dialect,
final @NonNull TargetDriverDialect targetDriverDialect,
@@ -151,7 +151,7 @@ public Connection connect(
throw new SQLLoginException(Messages.get("ConnectionProvider.noConnection"));
}
- return conn;
+ return new ConnectionInfo(conn, false);
}
protected Connection openConnection(
diff --git a/wrapper/src/main/java/software/amazon/jdbc/DriverConnectionProvider.java b/wrapper/src/main/java/software/amazon/jdbc/DriverConnectionProvider.java
index e2a04e399..1cd8891b3 100644
--- a/wrapper/src/main/java/software/amazon/jdbc/DriverConnectionProvider.java
+++ b/wrapper/src/main/java/software/amazon/jdbc/DriverConnectionProvider.java
@@ -108,11 +108,11 @@ public HostSpec getHostSpecByStrategy(
* @param targetDriverDialect The target driver dialect
* @param hostSpec The HostSpec containing the host-port information for the host to connect to
* @param props The Properties to use for the connection
- * @return {@link Connection} resulting from the given connection information
+ * @return {@link ConnectionInfo} resulting from the given connection information
* @throws SQLException if an error occurs
*/
@Override
- public Connection connect(
+ public @NonNull ConnectionInfo connect(
final @NonNull String protocol,
final @NonNull Dialect dialect,
final @NonNull TargetDriverDialect targetDriverDialect,
@@ -197,7 +197,7 @@ public Connection connect(
if (conn == null) {
throw new SQLLoginException(Messages.get("ConnectionProvider.noConnection"));
}
- return conn;
+ return new ConnectionInfo(conn, false);
}
@Override
diff --git a/wrapper/src/main/java/software/amazon/jdbc/HikariPooledConnectionProvider.java b/wrapper/src/main/java/software/amazon/jdbc/HikariPooledConnectionProvider.java
index f0d58c841..25c608d31 100644
--- a/wrapper/src/main/java/software/amazon/jdbc/HikariPooledConnectionProvider.java
+++ b/wrapper/src/main/java/software/amazon/jdbc/HikariPooledConnectionProvider.java
@@ -241,7 +241,7 @@ public HostSpec getHostSpecByStrategy(
}
@Override
- public Connection connect(
+ public @NonNull ConnectionInfo connect(
@NonNull String protocol,
@NonNull Dialect dialect,
@NonNull TargetDriverDialect targetDriverDialect,
@@ -286,7 +286,7 @@ public Connection connect(
ds.setPassword(copy.getProperty(PropertyDefinition.PASSWORD.name));
- return ds.getConnection();
+ return new ConnectionInfo(ds.getConnection(), true);
}
// The pool key should always be retrieved using this method, because the username
diff --git a/wrapper/src/main/java/software/amazon/jdbc/PartialPluginService.java b/wrapper/src/main/java/software/amazon/jdbc/PartialPluginService.java
index 95fceb1fa..8fc761130 100644
--- a/wrapper/src/main/java/software/amazon/jdbc/PartialPluginService.java
+++ b/wrapper/src/main/java/software/amazon/jdbc/PartialPluginService.java
@@ -217,6 +217,7 @@ public ConnectionProvider getDefaultConnectionProvider() {
return this.connectionProviderManager.getDefaultProvider();
}
+ @Deprecated
public boolean isPooledConnectionProvider(HostSpec host, Properties props) {
final ConnectionProvider connectionProvider =
this.connectionProviderManager.getConnectionProvider(this.driverProtocol, host, props);
@@ -654,6 +655,26 @@ public boolean isPluginInUse(final Class extends ConnectionPlugin> pluginClazz
}
}
+ @Override
+ public Boolean isPooledConnection() {
+ // This service implementation doesn't support call context.
+ throw new UnsupportedOperationException(
+ Messages.get("PartialPluginService.unexpectedMethodCall", new Object[] {"getSessionStateService"}));
+ }
+
+ @Override
+ public void setIsPooledConnection(Boolean pooledConnection) {
+ // This service implementation doesn't support call context.
+ // Do nothing.
+ }
+
+ @Override
+ public void resetCallContext() {
+ // This service implementation doesn't support call context.
+ throw new UnsupportedOperationException(
+ Messages.get("PartialPluginService.unexpectedMethodCall", new Object[] {"getSessionStateService"}));
+ }
+
@Override
public T unwrap(Class iface) throws SQLException {
if (iface == PluginService.class) {
diff --git a/wrapper/src/main/java/software/amazon/jdbc/PluginManagerService.java b/wrapper/src/main/java/software/amazon/jdbc/PluginManagerService.java
index 79c9168da..2965fe821 100644
--- a/wrapper/src/main/java/software/amazon/jdbc/PluginManagerService.java
+++ b/wrapper/src/main/java/software/amazon/jdbc/PluginManagerService.java
@@ -16,7 +16,13 @@
package software.amazon.jdbc;
+import org.checkerframework.checker.nullness.qual.Nullable;
+
public interface PluginManagerService {
void setInTransaction(boolean inTransaction);
+
+ void setIsPooledConnection(@Nullable Boolean pooledConnection);
+
+ void resetCallContext();
}
diff --git a/wrapper/src/main/java/software/amazon/jdbc/PluginService.java b/wrapper/src/main/java/software/amazon/jdbc/PluginService.java
index 1ed394c27..aae57d7f4 100644
--- a/wrapper/src/main/java/software/amazon/jdbc/PluginService.java
+++ b/wrapper/src/main/java/software/amazon/jdbc/PluginService.java
@@ -233,6 +233,7 @@ Connection forceConnect(
ConnectionProvider getDefaultConnectionProvider();
+ @Deprecated
boolean isPooledConnectionProvider(HostSpec host, Properties props);
String getDriverProtocol();
@@ -248,4 +249,19 @@ Connection forceConnect(
T getPlugin(final Class pluginClazz);
boolean isPluginInUse(final Class extends ConnectionPlugin> pluginClazz);
+
+ // JDBC call context functions
+
+ /**
+ * Retrieves details about the most recent {@link PluginService#connect} or
+ * {@link PluginService#forceConnect} calls. Specifically indicates whether the
+ * returned connection was obtained from a connection pool or newly created.
+ *
+ * Note: The {@link ConnectionPlugin} must process or store this information during
+ * the current JDBC call, as these details will be reset before the next JDBC call
+ * is processed, or another {@link PluginService#connect} or {@link PluginService#forceConnect}
+ * is made.
+ *
+ */
+ @Nullable Boolean isPooledConnection();
}
diff --git a/wrapper/src/main/java/software/amazon/jdbc/PluginServiceImpl.java b/wrapper/src/main/java/software/amazon/jdbc/PluginServiceImpl.java
index ca4b0e5a8..21dd7bb3a 100644
--- a/wrapper/src/main/java/software/amazon/jdbc/PluginServiceImpl.java
+++ b/wrapper/src/main/java/software/amazon/jdbc/PluginServiceImpl.java
@@ -92,6 +92,9 @@ public class PluginServiceImpl implements PluginService, CanReleaseResources,
protected final ReentrantLock connectionSwitchLock = new ReentrantLock();
+ // JDBC call context members
+ protected Boolean pooledConnection = null;
+
public PluginServiceImpl(
@NonNull final FullServicesContainer servicesContainer,
@NonNull final Properties props,
@@ -244,6 +247,7 @@ public ConnectionProvider getDefaultConnectionProvider() {
return this.connectionProviderManager.getDefaultProvider();
}
+ @Deprecated
public boolean isPooledConnectionProvider(HostSpec host, Properties props) {
final ConnectionProvider connectionProvider =
this.connectionProviderManager.getConnectionProvider(this.driverProtocol, host, props);
@@ -790,6 +794,21 @@ public boolean isPluginInUse(final Class extends ConnectionPlugin> pluginClazz
}
}
+ @Override
+ public Boolean isPooledConnection() {
+ return this.pooledConnection;
+ }
+
+ @Override
+ public void setIsPooledConnection(Boolean pooledConnection) {
+ this.pooledConnection = pooledConnection;
+ }
+
+ @Override
+ public void resetCallContext() {
+ this.pooledConnection = null;
+ }
+
@Override
public T unwrap(Class iface) throws SQLException {
if (iface == PluginService.class) {
diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/DefaultConnectionPlugin.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/DefaultConnectionPlugin.java
index 66277275b..2427cc816 100644
--- a/wrapper/src/main/java/software/amazon/jdbc/plugin/DefaultConnectionPlugin.java
+++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/DefaultConnectionPlugin.java
@@ -31,6 +31,7 @@
import java.util.stream.Collectors;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
+import software.amazon.jdbc.ConnectionInfo;
import software.amazon.jdbc.ConnectionPlugin;
import software.amazon.jdbc.ConnectionProvider;
import software.amazon.jdbc.ConnectionProviderManager;
@@ -190,9 +191,9 @@ private Connection connectInternal(
TelemetryContext telemetryContext = telemetryFactory.openTelemetryContext(
connProvider.getTargetName(), TelemetryTraceLevel.NESTED);
- Connection conn;
+ ConnectionInfo connectionInfo;
try {
- conn = connProvider.connect(
+ connectionInfo = connProvider.connect(
driverProtocol,
this.pluginService.getDialect(),
this.pluginService.getTargetDriverDialect(),
@@ -204,14 +205,17 @@ private Connection connectInternal(
}
}
- this.connProviderManager.initConnection(conn, driverProtocol, hostSpec, props);
+ this.pluginManagerService.setIsPooledConnection(connectionInfo.isPooled());
+ this.connProviderManager.initConnection(connectionInfo.getConnection(), driverProtocol, hostSpec, props);
- this.pluginService.setAvailability(hostSpec.asAliases(), HostAvailability.AVAILABLE);
+ if (connectionInfo.getConnection() != null) {
+ this.pluginService.setAvailability(hostSpec.asAliases(), HostAvailability.AVAILABLE);
+ }
if (isInitialConnection) {
- this.pluginService.updateDialect(conn);
+ this.pluginService.updateDialect(connectionInfo.getConnection());
}
- return conn;
+ return connectionInfo.getConnection();
}
@Override
diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/readwritesplitting/ReadWriteSplittingPlugin.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/readwritesplitting/ReadWriteSplittingPlugin.java
index 2d25675e2..5975936c0 100644
--- a/wrapper/src/main/java/software/amazon/jdbc/plugin/readwritesplitting/ReadWriteSplittingPlugin.java
+++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/readwritesplitting/ReadWriteSplittingPlugin.java
@@ -268,7 +268,7 @@ private boolean isReader(final @NonNull HostSpec hostSpec) {
private void getNewWriterConnection(final HostSpec writerHostSpec) throws SQLException {
final Connection conn = this.pluginService.connect(writerHostSpec, this.properties, this);
- this.isWriterConnFromInternalPool = this.pluginService.isPooledConnectionProvider(writerHostSpec, this.properties);
+ this.isWriterConnFromInternalPool = Boolean.TRUE.equals(this.pluginService.isPooledConnection());
setWriterConnection(conn, writerHostSpec);
switchCurrentConnectionTo(this.writerConnection, writerHostSpec);
}
@@ -492,7 +492,7 @@ private void getNewReaderConnection() throws SQLException {
HostSpec hostSpec = this.pluginService.getHostSpecByStrategy(HostRole.READER, this.readerSelectorStrategy);
try {
conn = this.pluginService.connect(hostSpec, this.properties, this);
- this.isReaderConnFromInternalPool = this.pluginService.isPooledConnectionProvider(hostSpec, this.properties);
+ this.isReaderConnFromInternalPool = Boolean.TRUE.equals(this.pluginService.isPooledConnection());
readerHost = hostSpec;
break;
} catch (final SQLException e) {
diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/WrapperUtils.java b/wrapper/src/main/java/software/amazon/jdbc/util/WrapperUtils.java
index 0037bdbd9..f4b0babb7 100644
--- a/wrapper/src/main/java/software/amazon/jdbc/util/WrapperUtils.java
+++ b/wrapper/src/main/java/software/amazon/jdbc/util/WrapperUtils.java
@@ -217,6 +217,8 @@ public static T executeWithPlugins(
context.setAttribute("jdbcCall", jdbcMethod.methodName);
}
+ connectionWrapper.getServicesContainer().getPluginManagerService().resetCallContext();
+
// The target driver may block on Statement.getConnection().
if (jdbcMethod.shouldLockConnection && jdbcMethod.checkBoundedConnection) {
final Connection conn = WrapperUtils.getConnectionFromSqlObject(methodInvokeOn);
@@ -286,6 +288,8 @@ public static T executeWithPlugins(
context.setAttribute("jdbcCall", jdbcMethod.methodName);
}
+ connectionWrapper.getServicesContainer().getPluginManagerService().resetCallContext();
+
// The target driver may block on Statement.getConnection().
if (jdbcMethod.shouldLockConnection && jdbcMethod.checkBoundedConnection) {
final Connection conn = WrapperUtils.getConnectionFromSqlObject(methodInvokeOn);
diff --git a/wrapper/src/main/java/software/amazon/jdbc/wrapper/ConnectionWrapper.java b/wrapper/src/main/java/software/amazon/jdbc/wrapper/ConnectionWrapper.java
index c29f11c2a..b0e86574e 100644
--- a/wrapper/src/main/java/software/amazon/jdbc/wrapper/ConnectionWrapper.java
+++ b/wrapper/src/main/java/software/amazon/jdbc/wrapper/ConnectionWrapper.java
@@ -63,6 +63,7 @@ public class ConnectionWrapper implements Connection, CanReleaseResources {
protected final String originalUrl;
protected @Nullable ConfigurationProfile configurationProfile;
protected @Nullable Throwable openConnectionStacktrace;
+ protected FullServicesContainer servicesContainer;
public ConnectionWrapper(
@NonNull final FullServicesContainer servicesContainer,
@@ -71,6 +72,7 @@ public ConnectionWrapper(
@NonNull final String targetDriverProtocol,
@Nullable final ConfigurationProfile configurationProfile)
throws SQLException {
+ this.servicesContainer = servicesContainer;
this.pluginManager = servicesContainer.getConnectionPluginManager();
this.pluginService = servicesContainer.getPluginService();
this.hostListProviderService = servicesContainer.getHostListProviderService();
@@ -105,6 +107,10 @@ protected ConnectionWrapper(
init(props);
}
+ public FullServicesContainer getServicesContainer() {
+ return this.servicesContainer;
+ }
+
protected void init(final Properties props) throws SQLException {
if (this.pluginService.getCurrentConnection() == null) {
final Connection conn =
diff --git a/wrapper/src/test/java/software/amazon/jdbc/ConnectionPluginManagerTests.java b/wrapper/src/test/java/software/amazon/jdbc/ConnectionPluginManagerTests.java
index 8a880a39d..080fea20e 100644
--- a/wrapper/src/test/java/software/amazon/jdbc/ConnectionPluginManagerTests.java
+++ b/wrapper/src/test/java/software/amazon/jdbc/ConnectionPluginManagerTests.java
@@ -609,6 +609,11 @@ public void testTwoConnectionsDoNotBlockOneAnother() {
final ConnectionProvider mockConnectionProvider1 = Mockito.mock(ConnectionProvider.class);
final ConnectionWrapper mockConnectionWrapper1 = Mockito.mock(ConnectionWrapper.class);
final PluginService mockPluginService1 = Mockito.mock(PluginService.class);
+ final PluginManagerService mockPluginManagerService1 = Mockito.mock(PluginManagerService.class);
+ final FullServicesContainer mockServicesContainer1 = Mockito.mock(FullServicesContainer.class);
+ when(mockConnectionWrapper1.getServicesContainer()).thenReturn(mockServicesContainer1);
+ when(mockServicesContainer1.getPluginService()).thenReturn(mockPluginService1);
+ when(mockServicesContainer1.getPluginManagerService()).thenReturn(mockPluginManagerService1);
final TelemetryFactory mockTelemetryFactory1 = Mockito.mock(TelemetryFactory.class);
final Object object1 = new Object();
when(mockPluginService1.getTelemetryFactory()).thenReturn(mockTelemetryFactory1);
@@ -617,10 +622,14 @@ public void testTwoConnectionsDoNotBlockOneAnother() {
final ConnectionPluginManager pluginManager1 = new ConnectionPluginManager(
mockConnectionProvider1, null, testProperties, testPlugins, mockTelemetryFactory1);
-
final ConnectionProvider mockConnectionProvider2 = Mockito.mock(ConnectionProvider.class);
final ConnectionWrapper mockConnectionWrapper2 = Mockito.mock(ConnectionWrapper.class);
final PluginService mockPluginService2 = Mockito.mock(PluginService.class);
+ final PluginManagerService mockPluginManagerService2 = Mockito.mock(PluginManagerService.class);
+ final FullServicesContainer mockServicesContainer2 = Mockito.mock(FullServicesContainer.class);
+ when(mockConnectionWrapper2.getServicesContainer()).thenReturn(mockServicesContainer2);
+ when(mockServicesContainer2.getPluginService()).thenReturn(mockPluginService2);
+ when(mockServicesContainer2.getPluginManagerService()).thenReturn(mockPluginManagerService2);
final TelemetryFactory mockTelemetryFactory2 = Mockito.mock(TelemetryFactory.class);
final Object object2 = new Object();
when(mockPluginService2.getTelemetryFactory()).thenReturn(mockTelemetryFactory2);
diff --git a/wrapper/src/test/java/software/amazon/jdbc/HikariPooledConnectionProviderTest.java b/wrapper/src/test/java/software/amazon/jdbc/HikariPooledConnectionProviderTest.java
index 6e5844ccf..89f29acdd 100644
--- a/wrapper/src/test/java/software/amazon/jdbc/HikariPooledConnectionProviderTest.java
+++ b/wrapper/src/test/java/software/amazon/jdbc/HikariPooledConnectionProviderTest.java
@@ -141,13 +141,26 @@ void testConnectWithDefaultMapping() throws SQLException {
Properties props = new Properties();
props.setProperty(PropertyDefinition.USER.name, user1);
props.setProperty(PropertyDefinition.PASSWORD.name, password);
- try (Connection conn = provider.connect(protocol, mockDialect, mockTargetDriverDialect, mockHostSpec, props)) {
+
+ ConnectionInfo connectionInfo = null;
+ try {
+ connectionInfo =
+ provider.connect(protocol, mockDialect, mockTargetDriverDialect, mockHostSpec, props);
+ Connection conn = connectionInfo.getConnection();
assertEquals(mockConnection, conn);
assertEquals(1, provider.getHostCount());
final Set hosts = provider.getHosts();
assertEquals(expectedUrls, hosts);
final Set keys = provider.getKeys();
assertEquals(expectedKeys, keys);
+ } finally {
+ if (connectionInfo != null && connectionInfo.getConnection() != null) {
+ try {
+ connectionInfo.getConnection().close();
+ } catch (Exception ex) {
+ // ignore
+ }
+ }
}
}
@@ -166,11 +179,24 @@ void testConnectWithCustomMapping() throws SQLException {
Properties props = new Properties();
props.setProperty(PropertyDefinition.USER.name, user1);
props.setProperty(PropertyDefinition.PASSWORD.name, password);
- try (Connection conn = provider.connect(protocol, mockDialect, mockTargetDriverDialect, mockHostSpec, props)) {
+
+ ConnectionInfo connectionInfo = null;
+ try {
+ connectionInfo =
+ provider.connect(protocol, mockDialect, mockTargetDriverDialect, mockHostSpec, props);
+ Connection conn = connectionInfo.getConnection();
assertEquals(mockConnection, conn);
assertEquals(1, provider.getHostCount());
final Set keys = provider.getKeys();
assertEquals(expectedKeys, keys);
+ } finally {
+ if (connectionInfo != null && connectionInfo.getConnection() != null) {
+ try {
+ connectionInfo.getConnection().close();
+ } catch (Exception ex) {
+ // ignore
+ }
+ }
}
}
diff --git a/wrapper/src/test/java/software/amazon/jdbc/plugin/DefaultConnectionPluginTest.java b/wrapper/src/test/java/software/amazon/jdbc/plugin/DefaultConnectionPluginTest.java
index 9893e7c95..eaf9d11f1 100644
--- a/wrapper/src/test/java/software/amazon/jdbc/plugin/DefaultConnectionPluginTest.java
+++ b/wrapper/src/test/java/software/amazon/jdbc/plugin/DefaultConnectionPluginTest.java
@@ -43,6 +43,7 @@
import org.junit.jupiter.params.provider.MethodSource;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import software.amazon.jdbc.ConnectionInfo;
import software.amazon.jdbc.ConnectionProvider;
import software.amazon.jdbc.ConnectionProviderManager;
import software.amazon.jdbc.HostSpec;
@@ -77,7 +78,7 @@ class DefaultConnectionPluginTest {
private AutoCloseable closeable;
@BeforeEach
- void setUp() {
+ void setUp() throws SQLException {
closeable = MockitoAnnotations.openMocks(this);
when(pluginService.getTelemetryFactory()).thenReturn(mockTelemetryFactory);
@@ -88,6 +89,8 @@ void setUp() {
when(mockTelemetryFactory.createGauge(anyString(), any(GaugeCallable.class))).thenReturn(mockTelemetryGauge);
when(mockConnectionProviderManager.getConnectionProvider(anyString(), any(), any()))
.thenReturn(connectionProvider);
+ when(connectionProvider.connect(anyString(), any(), any(), any(), any()))
+ .thenReturn(new ConnectionInfo(conn, false));
plugin = new DefaultConnectionPlugin(
pluginService, connectionProvider, pluginManagerService, mockConnectionProviderManager);
diff --git a/wrapper/src/test/java/software/amazon/jdbc/plugin/dev/DeveloperConnectionPluginTest.java b/wrapper/src/test/java/software/amazon/jdbc/plugin/dev/DeveloperConnectionPluginTest.java
index 1f8c387a7..a10a986ce 100644
--- a/wrapper/src/test/java/software/amazon/jdbc/plugin/dev/DeveloperConnectionPluginTest.java
+++ b/wrapper/src/test/java/software/amazon/jdbc/plugin/dev/DeveloperConnectionPluginTest.java
@@ -37,6 +37,7 @@
import org.junit.jupiter.api.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import software.amazon.jdbc.ConnectionInfo;
import software.amazon.jdbc.ConnectionPluginManager;
import software.amazon.jdbc.ConnectionProvider;
import software.amazon.jdbc.HostSpec;
@@ -91,7 +92,8 @@ void init() throws SQLException {
mockPluginService,
mockPluginService);
- when(mockConnectionProvider.connect(any(), any(), any(), any(), any())).thenReturn(mockConnection);
+ when(mockConnectionProvider.connect(any(), any(), any(), any(), any()))
+ .thenReturn(new ConnectionInfo(mockConnection, false));
when(mockConnectCallback.getExceptionToRaise(any(), any(), any(), anyBoolean())).thenReturn(null);
when(mockConnectionPluginManager.getTelemetryFactory()).thenReturn(mockTelemetryFactory);
diff --git a/wrapper/src/test/java/software/amazon/jdbc/plugin/readwritesplitting/ReadWriteSplittingPluginTest.java b/wrapper/src/test/java/software/amazon/jdbc/plugin/readwritesplitting/ReadWriteSplittingPluginTest.java
index c7c7bdc1b..976fcf2ea 100644
--- a/wrapper/src/test/java/software/amazon/jdbc/plugin/readwritesplitting/ReadWriteSplittingPluginTest.java
+++ b/wrapper/src/test/java/software/amazon/jdbc/plugin/readwritesplitting/ReadWriteSplittingPluginTest.java
@@ -567,7 +567,7 @@ public void testClosePooledReaderConnectionAfterSetReadOnly() throws SQLExceptio
.when(this.mockPluginService).getCurrentHostSpec();
doReturn(mockReaderConn1).when(mockPluginService).connect(readerHostSpec1, null);
when(mockPluginService.getDriverProtocol()).thenReturn("jdbc:postgresql://");
- when(mockPluginService.isPooledConnectionProvider(any(), any())).thenReturn(true);
+ when(mockPluginService.isPooledConnection()).thenReturn(true);
final ReadWriteSplittingPlugin plugin = new ReadWriteSplittingPlugin(
mockPluginService,
@@ -593,7 +593,7 @@ public void testClosePooledWriterConnectionAfterSetReadOnly() throws SQLExceptio
.when(this.mockPluginService).getCurrentHostSpec();
doReturn(mockWriterConn).when(mockPluginService).connect(writerHostSpec, null);
when(mockPluginService.getDriverProtocol()).thenReturn("jdbc:postgresql://");
- when(mockPluginService.isPooledConnectionProvider(any(), any())).thenReturn(true);
+ when(mockPluginService.isPooledConnection()).thenReturn(true);
final ReadWriteSplittingPlugin plugin = new ReadWriteSplittingPlugin(
mockPluginService,
diff --git a/wrapper/src/test/java/software/amazon/jdbc/util/WrapperUtilsTest.java b/wrapper/src/test/java/software/amazon/jdbc/util/WrapperUtilsTest.java
index afcae7530..3a285939a 100644
--- a/wrapper/src/test/java/software/amazon/jdbc/util/WrapperUtilsTest.java
+++ b/wrapper/src/test/java/software/amazon/jdbc/util/WrapperUtilsTest.java
@@ -45,6 +45,8 @@
import software.amazon.jdbc.ConnectionPluginManager;
import software.amazon.jdbc.JdbcCallable;
import software.amazon.jdbc.JdbcMethod;
+import software.amazon.jdbc.PluginManagerService;
+import software.amazon.jdbc.PluginService;
import software.amazon.jdbc.util.telemetry.TelemetryContext;
import software.amazon.jdbc.util.telemetry.TelemetryFactory;
import software.amazon.jdbc.wrapper.CallableStatementWrapper;
@@ -55,7 +57,10 @@
public class WrapperUtilsTest {
@Mock ConnectionWrapper mockConnectionWrapper;
+ @Mock FullServicesContainer mockServicesContainer;
@Mock ConnectionPluginManager mockPluginManager;
+ @Mock PluginService mockPluginService;
+ @Mock PluginManagerService mockPluginManagerService;
@Mock TelemetryFactory mockTelemetryFactory;
@Mock TelemetryContext mockTelemetryContext;
@Mock Object object;
@@ -73,6 +78,10 @@ void init() {
when(mockPluginManager.getTelemetryFactory()).thenReturn(mockTelemetryFactory);
when(mockTelemetryFactory.openTelemetryContext(anyString(), any())).thenReturn(mockTelemetryContext);
when(mockTelemetryFactory.openTelemetryContext(eq(null), any())).thenReturn(mockTelemetryContext);
+ when(mockConnectionWrapper.getServicesContainer()).thenReturn(mockServicesContainer);
+ when(mockServicesContainer.getConnectionPluginManager()).thenReturn(mockPluginManager);
+ when(mockServicesContainer.getPluginService()).thenReturn(mockPluginService);
+ when(mockServicesContainer.getPluginManagerService()).thenReturn(mockPluginManagerService);
}
private void mockExecuteReturnValue(Object returnValue) {
From 050a952ffbe051d249ca1bc2eabae1fc83d61fbf Mon Sep 17 00:00:00 2001
From: sergiyvamz <75754709+sergiyvamz@users.noreply.github.com>
Date: Wed, 5 Nov 2025 14:30:03 -0800
Subject: [PATCH 12/38] docs: deprecate auroraStaleDns plugin (#1590)
---
.../CompatibilityCrossPlugins.md | 8 +++----
.../CompatibilityDatabaseTypes.md | 14 +++++------
.../CompatibilityEndpoints.md | 24 +++++++++----------
.../UsingTheJdbcDriver.md | 4 ++--
.../plugin/staledns/AuroraStaleDnsPlugin.java | 4 +++-
5 files changed, 28 insertions(+), 26 deletions(-)
diff --git a/docs/using-the-jdbc-driver/CompatibilityCrossPlugins.md b/docs/using-the-jdbc-driver/CompatibilityCrossPlugins.md
index 0f20293f0..d40aee79d 100644
--- a/docs/using-the-jdbc-driver/CompatibilityCrossPlugins.md
+++ b/docs/using-the-jdbc-driver/CompatibilityCrossPlugins.md
@@ -14,7 +14,7 @@
| [awsSecretsManager](./using-plugins/UsingTheAwsSecretsManagerPlugin.md) | ✓ | ✓ | ✓ | ✓ |
| [federatedAuth](./using-plugins/UsingTheFederatedAuthPlugin.md) | ✓ | ✓ | ✓ | ✓ |
| [okta](./using-plugins/UsingTheOktaAuthPlugin.md) | ✓ | ✓ | ✓ | ✓ |
-| auroraStaleDns | ✓ | ✓ | ✓ | ✓ |
+| ~~auroraStaleDns~~ | ✓ | ✓ | ✓ | ✓ |
| [readWriteSplitting](./using-plugins/UsingTheReadWriteSplittingPlugin.md) | ✓ | ✓ | ✓ | ✓ |
| [auroraConnectionTracker](./using-plugins/UsingTheAuroraConnectionTrackerPlugin.md) | ✓ | ✓ | ✓ | ✓ |
| [driverMetaData](./using-plugins/UsingTheDriverMetadataConnectionPlugin.md) | ✓ | ✓ | ✓ | ✓ |
@@ -36,7 +36,7 @@
| [awsSecretsManager](./using-plugins/UsingTheAwsSecretsManagerPlugin.md) | ✓ | ✓ | ✓ | ✓ |
| [federatedAuth](./using-plugins/UsingTheFederatedAuthPlugin.md) | ✓ | ✓ | ✓ | ✓ |
| [okta](./using-plugins/UsingTheOktaAuthPlugin.md) | ✓ | ✓ | ✓ | ✓ |
-| auroraStaleDns | ✓ | ✓ | ✓ | ✓ |
+| ~~auroraStaleDns~~ | ✓ | ✓ | ✓ | ✓ |
| [readWriteSplitting](./using-plugins/UsingTheReadWriteSplittingPlugin.md) | ✓ | ✓ | ✓ | ✓ |
| [auroraConnectionTracker](./using-plugins/UsingTheAuroraConnectionTrackerPlugin.md) | ✓ | ✓ | ✓ | ✓ |
| [driverMetaData](./using-plugins/UsingTheDriverMetadataConnectionPlugin.md) | ✓ | ✓ | ✓ | ✓ |
@@ -54,7 +54,7 @@
| [awsSecretsManager](./using-plugins/UsingTheAwsSecretsManagerPlugin.md) | ✗ | | | |
| [federatedAuth](./using-plugins/UsingTheFederatedAuthPlugin.md) | ✗ | ✗ | | |
| [okta](./using-plugins/UsingTheOktaAuthPlugin.md) | ✗ | ✗ | ✗ | |
-| auroraStaleDns | ✓ | ✓ | ✓ | ✓ |
+| ~~auroraStaleDns~~ | ✓ | ✓ | ✓ | ✓ |
| [readWriteSplitting](./using-plugins/UsingTheReadWriteSplittingPlugin.md) | ✓ | ✓ | ✓ | ✓ |
| [auroraConnectionTracker](./using-plugins/UsingTheAuroraConnectionTrackerPlugin.md) | ✓ | ✓ | ✓ | ✓ |
| [driverMetaData](./using-plugins/UsingTheDriverMetadataConnectionPlugin.md) | ✓ | ✓ | ✓ | ✓ |
@@ -67,7 +67,7 @@
-| Plugin codes / Plugin codes | auroraStaleDns | [readWriteSplitting](./using-plugins/UsingTheReadWriteSplittingPlugin.md) | [auroraConnectionTracker](./using-plugins/UsingTheAuroraConnectionTrackerPlugin.md) | [driverMetaData](./using-plugins/UsingTheDriverMetadataConnectionPlugin.md) |
+| Plugin codes / Plugin codes | ~~auroraStaleDns~~ | [readWriteSplitting](./using-plugins/UsingTheReadWriteSplittingPlugin.md) | [auroraConnectionTracker](./using-plugins/UsingTheAuroraConnectionTrackerPlugin.md) | [driverMetaData](./using-plugins/UsingTheDriverMetadataConnectionPlugin.md) |
|---------------------------------------------------------------------------------------|----------------------------------------------------------|---------------------------------------------------------------------------|-------------------------------------------------------------------------------------|-----------------------------------------------------------------------------|
| [readWriteSplitting](./using-plugins/UsingTheReadWriteSplittingPlugin.md) | ✓ | | | |
| [auroraConnectionTracker](./using-plugins/UsingTheAuroraConnectionTrackerPlugin.md) | ✓ | ✓ | | |
diff --git a/docs/using-the-jdbc-driver/CompatibilityDatabaseTypes.md b/docs/using-the-jdbc-driver/CompatibilityDatabaseTypes.md
index 733af5f34..0aeb66b9d 100644
--- a/docs/using-the-jdbc-driver/CompatibilityDatabaseTypes.md
+++ b/docs/using-the-jdbc-driver/CompatibilityDatabaseTypes.md
@@ -8,20 +8,20 @@
| customEndpoint | ✓ | ✓ | ✓ |
| [efm](./using-plugins/UsingTheHostMonitoringPlugin.md) | ✓ | ✓ | ✓ |
| [efm2](./using-plugins/UsingTheHostMonitoringPlugin.md#host-monitoring-plugin-v2) | ✓ | ✓ | ✓ |
-| [failover](./using-plugins/UsingTheFailoverPlugin.md) | ✓ | ✓ | ✓ |
-| [failover2](./using-plugins/UsingTheFailover2Plugin.md) | ✓ | ✓ | ✓ |
+| [failover](./using-plugins/UsingTheFailoverPlugin.md) | ✓ | ✓ | ✓ |
+| [failover2](./using-plugins/UsingTheFailover2Plugin.md) | ✓ | ✓ | ✓ |
| [iam](./using-plugins/UsingTheIamAuthenticationPlugin.md) | ✓ | ✓ | ✓ |
| [awsSecretsManager](./using-plugins/UsingTheAwsSecretsManagerPlugin.md) | ✓ | ✓ | ✓ |
| [federatedAuth](./using-plugins/UsingTheFederatedAuthPlugin.md) | ✓ | ✓ | ✓ |
| [okta](./using-plugins/UsingTheOktaAuthPlugin.md) | ✓ | ✓ | ✓ |
-| auroraStaleDns | ✓ | ✓ | ✓ |
-| [readWriteSplitting](./using-plugins/UsingTheReadWriteSplittingPlugin.md) | ✓ | ✓ | ✓ |
+| ~~auroraStaleDns~~ | ✓ | ✓ | ✓ |
+| [readWriteSplitting](./using-plugins/UsingTheReadWriteSplittingPlugin.md) | ✓ | ✓ | ✓ |
| [auroraConnectionTracker](./using-plugins/UsingTheAuroraConnectionTrackerPlugin.md) | ✓ | ✓ | ✓ |
| [driverMetaData](./using-plugins/UsingTheDriverMetadataConnectionPlugin.md) | ✓ | ✓ | ✓ |
| connectTime | ✓ | ✓ | ✓ |
| [dev](./using-plugins/UsingTheDeveloperPlugin.md) | ✓ | ✓ | ✓ |
-| fastestResponseStrategy | ✓ | ✓ | ✓ |
-| [initialConnection](./using-plugins/UsingTheAuroraInitialConnectionStrategyPlugin.md) | ✓ | ✓ | ✓ |
+| fastestResponseStrategy | ✓ | ✓ | ✓ |
+| [initialConnection](./using-plugins/UsingTheAuroraInitialConnectionStrategyPlugin.md) | ✓ | ✓ | ✓ |
| [limitless](./using-plugins/UsingTheLimitlessConnectionPlugin.md) | ✗ | ✓ (PostgreSQL only) | ✓ |
| [bg](./using-plugins/UsingTheBlueGreenPlugin.md) | ✗ | ✓ | ✓ |
@@ -41,7 +41,7 @@
| [awsSecretsManager](./using-plugins/UsingTheAwsSecretsManagerPlugin.md) | ✓ | ✓ | ✗ |
| [federatedAuth](./using-plugins/UsingTheFederatedAuthPlugin.md) | ✓ | ✓ | ✗ |
| [okta](./using-plugins/UsingTheOktaAuthPlugin.md) | ✓ | ✓ | ✗ |
-| auroraStaleDns | ✗ | ✗ | ✗ |
+| ~~auroraStaleDns~~ | ✗ | ✗ | ✗ |
| [readWriteSplitting](./using-plugins/UsingTheReadWriteSplittingPlugin.md) | ✗ | ✗ | ✗ |
| [auroraConnectionTracker](./using-plugins/UsingTheAuroraConnectionTrackerPlugin.md) | ✗ | ✗ | ✗ |
| [driverMetaData](./using-plugins/UsingTheDriverMetadataConnectionPlugin.md) | ✓ | ✓ | ✓ |
diff --git a/docs/using-the-jdbc-driver/CompatibilityEndpoints.md b/docs/using-the-jdbc-driver/CompatibilityEndpoints.md
index 3a5722371..56a2e8440 100644
--- a/docs/using-the-jdbc-driver/CompatibilityEndpoints.md
+++ b/docs/using-the-jdbc-driver/CompatibilityEndpoints.md
@@ -23,22 +23,22 @@ There are many different URL types (endpoints) that can be used with The AWS Adv
| logQuery | ✓ |
| dataCache | ✓ |
| customEndpoint | ✗ |
-| [efm](./using-plugins/UsingTheHostMonitoringPlugin.md) | ✓ (requires `initialConnection` plugin) |
-| [efm2](./using-plugins/UsingTheHostMonitoringPlugin.md#host-monitoring-plugin-v2) | ✓ (requires `initialConnection` plugin) |
-| [failover](./using-plugins/UsingTheFailoverPlugin.md) | ✓ |
-| [failover2](./using-plugins/UsingTheFailover2Plugin.md) | ✓ |
-| [iam](./using-plugins/UsingTheIamAuthenticationPlugin.md) | ✓ (requires `initialConnection` plugin) |
+| [efm](./using-plugins/UsingTheHostMonitoringPlugin.md) | ✓ (requires `initialConnection` plugin) |
+| [efm2](./using-plugins/UsingTheHostMonitoringPlugin.md#host-monitoring-plugin-v2) | ✓ (requires `initialConnection` plugin) |
+| [failover](./using-plugins/UsingTheFailoverPlugin.md) | ✓ |
+| [failover2](./using-plugins/UsingTheFailover2Plugin.md) | ✓ |
+| [iam](./using-plugins/UsingTheIamAuthenticationPlugin.md) | ✓ (requires `initialConnection` plugin) |
| [awsSecretsManager](./using-plugins/UsingTheAwsSecretsManagerPlugin.md) | ✓ |
| [federatedAuth](./using-plugins/UsingTheFederatedAuthPlugin.md) | ✓ |
| [okta](./using-plugins/UsingTheOktaAuthPlugin.md) | ✓ |
-| auroraStaleDns | ✓ |
-| [readWriteSplitting](./using-plugins/UsingTheReadWriteSplittingPlugin.md) | ✓ |
+| ~~auroraStaleDns~~ | ✓ |
+| [readWriteSplitting](./using-plugins/UsingTheReadWriteSplittingPlugin.md) | ✓ |
| [auroraConnectionTracker](./using-plugins/UsingTheAuroraConnectionTrackerPlugin.md) | ✓ |
| [driverMetaData](./using-plugins/UsingTheDriverMetadataConnectionPlugin.md) | ✓ |
| connectTime | ✓ |
| [dev](./using-plugins/UsingTheDeveloperPlugin.md) | ✓ |
-| fastestResponseStrategy | ✓ |
-| [initialConnection](./using-plugins/UsingTheAuroraInitialConnectionStrategyPlugin.md) | ✓ |
+| fastestResponseStrategy | ✓ |
+| [initialConnection](./using-plugins/UsingTheAuroraInitialConnectionStrategyPlugin.md) | ✓ |
| [limitless](./using-plugins/UsingTheLimitlessConnectionPlugin.md) | ✗ |
| [bg](./using-plugins/UsingTheBlueGreenPlugin.md) | ✗ |
@@ -58,7 +58,7 @@ There are many different URL types (endpoints) that can be used with The AWS Adv
| [awsSecretsManager](./using-plugins/UsingTheAwsSecretsManagerPlugin.md) | ✓ | ✓ | ✓ | ✓ |
| [federatedAuth](./using-plugins/UsingTheFederatedAuthPlugin.md) | ✓ | ✓ | ✓ | ✓ |
| [okta](./using-plugins/UsingTheOktaAuthPlugin.md) | ✓ | ✓ | ✓ | ✓ |
-| auroraStaleDns | ✓ | ✗ | ✗ | ✗ |
+| ~~auroraStaleDns~~ | ✓ | ✗ | ✗ | ✗ |
| [readWriteSplitting](./using-plugins/UsingTheReadWriteSplittingPlugin.md) | ✓ | ✓ | ✓ | ✓ |
| [auroraConnectionTracker](./using-plugins/UsingTheAuroraConnectionTrackerPlugin.md) | ✓ | ✓ | ✓ | ✓ |
| [driverMetaData](./using-plugins/UsingTheDriverMetadataConnectionPlugin.md) | ✓ | ✓ | ✓ | ✓ |
@@ -85,7 +85,7 @@ There are many different URL types (endpoints) that can be used with The AWS Adv
| [awsSecretsManager](./using-plugins/UsingTheAwsSecretsManagerPlugin.md) | ✓ | ✓ | ✓ | ✓ |
| [federatedAuth](./using-plugins/UsingTheFederatedAuthPlugin.md) | ✓ | ✓ | ✓ | ✓ |
| [okta](./using-plugins/UsingTheOktaAuthPlugin.md) | ✓ | ✓ | ✓ | ✓ |
-| auroraStaleDns | ✓ | ✗ | ✗ | ✗ |
+| ~~auroraStaleDns~~ | ✓ | ✗ | ✗ | ✗ |
| [readWriteSplitting](./using-plugins/UsingTheReadWriteSplittingPlugin.md) | ✓ | ✓ | ✗ | ✗ |
| [auroraConnectionTracker](./using-plugins/UsingTheAuroraConnectionTrackerPlugin.md) | ✓ | ✓ | ✓ | ✗ |
| [driverMetaData](./using-plugins/UsingTheDriverMetadataConnectionPlugin.md) | ✓ | ✓ | ✓ | ✓ |
@@ -113,7 +113,7 @@ There are many different URL types (endpoints) that can be used with The AWS Adv
| [awsSecretsManager](./using-plugins/UsingTheAwsSecretsManagerPlugin.md) | ✓ (requires special configuration) | ✓ (requires special configuration) |
| [federatedAuth](./using-plugins/UsingTheFederatedAuthPlugin.md) | ✓ (requires special configuration) | ✓ (requires special configuration) |
| [okta](./using-plugins/UsingTheOktaAuthPlugin.md) | ✓ (requires special configuration) | ✓ (requires special configuration) |
-| auroraStaleDns | ✗ | ✗ |
+| ~~auroraStaleDns~~ | ✗ | ✗ |
| [readWriteSplitting](./using-plugins/UsingTheReadWriteSplittingPlugin.md) | ✓ | ✓ |
| [auroraConnectionTracker](./using-plugins/UsingTheAuroraConnectionTrackerPlugin.md) | ✓ | ✓ |
| [driverMetaData](./using-plugins/UsingTheDriverMetadataConnectionPlugin.md) | ✓ | ✓ |
diff --git a/docs/using-the-jdbc-driver/UsingTheJdbcDriver.md b/docs/using-the-jdbc-driver/UsingTheJdbcDriver.md
index 2ef0a8b35..1a788b3ae 100644
--- a/docs/using-the-jdbc-driver/UsingTheJdbcDriver.md
+++ b/docs/using-the-jdbc-driver/UsingTheJdbcDriver.md
@@ -98,7 +98,7 @@ These parameters are applicable to any instance of the AWS Advanced JDBC Wrapper
| `resetSessionStateOnClose` | `Boolean` | No | Enables resetting the session state before closing connection. | `true` |
| `rollbackOnSwitch` | `Boolean` | No | Enables rolling back a current transaction, if any in effect, before switching to a new connection. | `true` |
| `awsProfile` | `String` | No | Allows users to specify a profile name for AWS credentials. This parameter is used by plugins that require AWS credentials, like the [IAM Authentication Connection Plugin](./using-plugins/UsingTheIamAuthenticationPlugin.md) and the [AWS Secrets Manager Connection Plugin](./using-plugins/UsingTheAwsSecretsManagerPlugin.md). | `null` |
-| ~~`enableGreenNodeReplacement`~~ | `Boolean` | No | **Deprecated. Use `bg` plugin instead.** Enables replacing a green node host name with the original host name when the green host DNS doesn't exist anymore after a blue/green switchover. Refer to [Overview of Amazon RDS Blue/Green Deployments](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/blue-green-deployments-overview.html) for more details about green and blue nodes. | `false` |
+| ~~`enableGreenNodeReplacement`~~ | `Boolean` | No | **Deprecated. Use `bg` plugin instead.** Enables replacing a green node host name with the original host name when the green host DNS doesn't exist anymore after a blue/green switchover. Refer to [Overview of Amazon RDS Blue/Green Deployments](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/blue-green-deployments-overview.html) for more details about green and blue nodes. | `false` |
| `wrapperCaseSensitive`,
`wrappercasesensitive` | `Boolean` | No | Allows the driver to change case sensitivity for parameter names in the connection string and in connection properties. Set parameter to `false` to allow case-insensitive parameter names. | `true` |
| `skipWrappingForPackages` | `String` | No | Register Java package names (separated by comma) which will be left unwrapped. This setting modifies all future connections established by the driver, not just a particular connection. | `com.pgvector` |
@@ -211,7 +211,7 @@ The AWS Advanced JDBC Wrapper has several built-in plugins that are available to
| [AWS Secrets Manager Connection Plugin](./using-plugins/UsingTheAwsSecretsManagerPlugin.md) | `awsSecretsManager` | Any database | Enables fetching database credentials from the AWS Secrets Manager service. | [Jackson Databind](https://central.sonatype.com/artifact/com.fasterxml.jackson.core/jackson-databind)
[AWS Secrets Manager](https://central.sonatype.com/artifact/software.amazon.awssdk/secretsmanager) |
| [Federated Authentication Plugin](./using-plugins/UsingTheFederatedAuthPlugin.md) | `federatedAuth` | Aurora, RDS Multi-AZ DB Cluster | Enables users to authenticate using Federated Identity and then connect to their Amazon Aurora Cluster using AWS Identity and Access Management (IAM). | [Jackson Databind](https://central.sonatype.com/artifact/com.fasterxml.jackson.core/jackson-databind)
[AWS Java SDK RDS v2.7.x](https://central.sonatype.com/artifact/software.amazon.awssdk/rds)
[AWS Java SDK STS v2.7.x](https://central.sonatype.com/artifact/software.amazon.awssdk/sts) |
| [Okta Authentication Plugin](./using-plugins/UsingTheOktaAuthPlugin.md) | `okta` | Aurora, RDS Multi-AZ DB Cluster | Enables users to authenticate using Federated Identity and then connect to their Amazon Aurora Cluster using AWS Identity and Access Management (IAM). | [Jackson Databind](https://central.sonatype.com/artifact/com.fasterxml.jackson.core/jackson-databind)
[AWS Java SDK RDS v2.7.x](https://central.sonatype.com/artifact/software.amazon.awssdk/rds)
[AWS Java SDK STS v2.7.x](https://central.sonatype.com/artifact/software.amazon.awssdk/sts) |
-| Aurora Stale DNS Plugin | `auroraStaleDns` | Aurora | Prevents incorrectly opening a new connection to an old writer node when DNS records have not yet updated after a recent failover event.
:warning:**Note:** Contrary to `failover` plugin, `auroraStaleDns` plugin doesn't implement failover support itself. It helps to eliminate opening wrong connections to an old writer node after cluster failover is completed.
:warning:**Note:** This logic is already included in `failover` plugin so you can omit using both plugins at the same time. | None |
+| ~~Aurora Stale DNS Plugin~~ | `auroraStaleDns` | Aurora | **Deprecated**. Use `initialConnection` plugin instead.
Prevents incorrectly opening a new connection to an old writer node when DNS records have not yet updated after a recent failover event.
:warning:**Note:** Contrary to `failover` plugin, `auroraStaleDns` plugin doesn't implement failover support itself. It helps to eliminate opening wrong connections to an old writer node after cluster failover is completed.
:warning:**Note:** This logic is already included in `failover` plugin so you can omit using both plugins at the same time. | None |
| [Aurora Connection Tracker Plugin](./using-plugins/UsingTheAuroraConnectionTrackerPlugin.md) | `auroraConnectionTracker` | Aurora, RDS Multi-AZ DB Cluster | Tracks all the opened connections. In the event of a cluster failover, the plugin will close all the impacted connections to the node. This plugin is enabled by default. | None |
| [Driver Metadata Connection Plugin](./using-plugins/UsingTheDriverMetadataConnectionPlugin.md) | `driverMetaData` | Any database | Allows user application to override the return value of `DatabaseMetaData#getDriverName` | None |
| [Read Write Splitting Plugin](./using-plugins/UsingTheReadWriteSplittingPlugin.md) | `readWriteSplitting` | Aurora | Enables read write splitting functionality where users can switch between database reader and writer instances. | None |
diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/staledns/AuroraStaleDnsPlugin.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/staledns/AuroraStaleDnsPlugin.java
index a5babfc3c..6345c27fa 100644
--- a/wrapper/src/main/java/software/amazon/jdbc/plugin/staledns/AuroraStaleDnsPlugin.java
+++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/staledns/AuroraStaleDnsPlugin.java
@@ -34,7 +34,9 @@
import software.amazon.jdbc.plugin.AbstractConnectionPlugin;
/**
- * After Aurora DB cluster fail over is completed and a cluster has elected a new writer node, the corresponding
+ * Deprecated. Use 'initialConnection' plugin instead.
+ *
+ * After Aurora DB cluster fail over is completed and a cluster has elected a new writer node, the corresponding
* cluster (writer) endpoint contains stale data and points to an old writer node. That old writer node plays
* a reader role after fail over and connecting with the cluster endpoint connects to it. In such case a user
* application expects a writer connection but practically gets connected to a reader. Any DML statements fail
From 9b8aa0b3f5f10467c6e5af5a885cc2299d7d17ef Mon Sep 17 00:00:00 2001
From: sergiyvamz <75754709+sergiyvamz@users.noreply.github.com>
Date: Wed, 5 Nov 2025 14:30:47 -0800
Subject: [PATCH 13/38] feat: add initialConnection plugin to default plugin
list (#1592)
---
docs/using-the-jdbc-driver/UsingTheJdbcDriver.md | 2 +-
.../amazon/jdbc/ConnectionPluginChainBuilder.java | 2 +-
.../AuroraInitialConnectionStrategyPlugin.java | 3 ---
.../aws_advanced_jdbc_wrapper_messages.properties | 1 -
.../amazon/jdbc/ConnectionPluginManagerTests.java | 12 +++++++-----
5 files changed, 9 insertions(+), 11 deletions(-)
diff --git a/docs/using-the-jdbc-driver/UsingTheJdbcDriver.md b/docs/using-the-jdbc-driver/UsingTheJdbcDriver.md
index 1a788b3ae..a74c32dd2 100644
--- a/docs/using-the-jdbc-driver/UsingTheJdbcDriver.md
+++ b/docs/using-the-jdbc-driver/UsingTheJdbcDriver.md
@@ -111,7 +111,7 @@ Plugins are loaded and managed through the Connection Plugin Manager and may be
| Parameter | Value | Required | Description | Default Value |
|-----------------------------------|-----------|----------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------|
-| `wrapperPlugins` | `String` | No | Comma separated list of connection plugin codes.
Example: `failover,efm2` | `auroraConnectionTracker,failover2,efm2` |
+| `wrapperPlugins` | `String` | No | Comma separated list of connection plugin codes.
Example: `failover,efm2` | `initialConnection,auroraConnectionTracker,failover2,efm2` |
| `autoSortWrapperPluginOrder` | `Boolean` | No | Allows the AWS Advanced JDBC Wrapper to sort connection plugins to prevent plugin misconfiguration. Allows a user to provide a custom plugin order if needed. | `true` |
| `wrapperProfileName` | `String` | No | Driver configuration profile name. Instead of listing plugin codes with `wrapperPlugins`, the driver profile can be set with this parameter.
Example: See [below](#configuration-profiles). | `null` |
diff --git a/wrapper/src/main/java/software/amazon/jdbc/ConnectionPluginChainBuilder.java b/wrapper/src/main/java/software/amazon/jdbc/ConnectionPluginChainBuilder.java
index 952b00936..3f1d89eac 100644
--- a/wrapper/src/main/java/software/amazon/jdbc/ConnectionPluginChainBuilder.java
+++ b/wrapper/src/main/java/software/amazon/jdbc/ConnectionPluginChainBuilder.java
@@ -126,7 +126,7 @@ public class ConnectionPluginChainBuilder {
protected static final ConcurrentMap, ConnectionPluginFactory>
pluginFactoriesByClass = new ConcurrentHashMap<>();
- protected static final String DEFAULT_PLUGINS = "auroraConnectionTracker,failover2,efm2";
+ protected static final String DEFAULT_PLUGINS = "initialConnection,auroraConnectionTracker,failover2,efm2";
/*
Internal class used for plugin factory sorting. It holds a reference to a plugin
diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/AuroraInitialConnectionStrategyPlugin.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/AuroraInitialConnectionStrategyPlugin.java
index 4655a48be..6655d2978 100644
--- a/wrapper/src/main/java/software/amazon/jdbc/plugin/AuroraInitialConnectionStrategyPlugin.java
+++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/AuroraInitialConnectionStrategyPlugin.java
@@ -130,9 +130,6 @@ public void initHostProvider(
final JdbcCallable initHostProviderFunc) throws SQLException {
this.hostListProviderService = hostListProviderService;
- if (hostListProviderService.isStaticHostListProvider()) {
- throw new SQLException(Messages.get("AuroraInitialConnectionStrategyPlugin.requireDynamicProvider"));
- }
initHostProviderFunc.call();
}
diff --git a/wrapper/src/main/resources/aws_advanced_jdbc_wrapper_messages.properties b/wrapper/src/main/resources/aws_advanced_jdbc_wrapper_messages.properties
index 3f17a3979..6f5b29760 100644
--- a/wrapper/src/main/resources/aws_advanced_jdbc_wrapper_messages.properties
+++ b/wrapper/src/main/resources/aws_advanced_jdbc_wrapper_messages.properties
@@ -361,7 +361,6 @@ MysqlConnectorJDriverHelper.canNotRegister=Can''t register driver com.mysql.cj.
MariadbDriverHelper.canNotRegister=Can''t register driver org.mariadb.jdbc.Driver.
AuroraInitialConnectionStrategyPlugin.unsupportedStrategy=Unsupported host selection strategy ''{0}''.
-AuroraInitialConnectionStrategyPlugin.requireDynamicProvider=Dynamic host list provider is required.
NodeResponseTimeMonitor.stopped=Stopped Response time thread for node ''{0}''.
NodeResponseTimeMonitor.responseTime=Response time for ''{0}'': {1} ms
diff --git a/wrapper/src/test/java/software/amazon/jdbc/ConnectionPluginManagerTests.java b/wrapper/src/test/java/software/amazon/jdbc/ConnectionPluginManagerTests.java
index 080fea20e..735e83eac 100644
--- a/wrapper/src/test/java/software/amazon/jdbc/ConnectionPluginManagerTests.java
+++ b/wrapper/src/test/java/software/amazon/jdbc/ConnectionPluginManagerTests.java
@@ -53,6 +53,7 @@
import software.amazon.jdbc.mock.TestPluginThrowException;
import software.amazon.jdbc.mock.TestPluginTwo;
import software.amazon.jdbc.plugin.AuroraConnectionTrackerPlugin;
+import software.amazon.jdbc.plugin.AuroraInitialConnectionStrategyPlugin;
import software.amazon.jdbc.plugin.DefaultConnectionPlugin;
import software.amazon.jdbc.plugin.LogQueryConnectionPlugin;
import software.amazon.jdbc.plugin.efm2.HostMonitoringConnectionPlugin;
@@ -565,12 +566,13 @@ public void testDefaultPlugins() throws SQLException {
testProperties, mockTelemetryFactory, mockConnectionProvider, null));
target.initPlugins(mockServicesContainer, configurationProfile);
- assertEquals(4, target.plugins.size());
- assertEquals(AuroraConnectionTrackerPlugin.class, target.plugins.get(0).getClass());
+ assertEquals(5, target.plugins.size());
+ assertEquals(AuroraInitialConnectionStrategyPlugin.class, target.plugins.get(0).getClass());
+ assertEquals(AuroraConnectionTrackerPlugin.class, target.plugins.get(1).getClass());
assertEquals(software.amazon.jdbc.plugin.failover2.FailoverConnectionPlugin.class,
- target.plugins.get(1).getClass());
- assertEquals(HostMonitoringConnectionPlugin.class, target.plugins.get(2).getClass());
- assertEquals(DefaultConnectionPlugin.class, target.plugins.get(3).getClass());
+ target.plugins.get(2).getClass());
+ assertEquals(HostMonitoringConnectionPlugin.class, target.plugins.get(3).getClass());
+ assertEquals(DefaultConnectionPlugin.class, target.plugins.get(4).getClass());
}
@Test
From 70130422df317d89e7cc3988f03cb6818ae8294b Mon Sep 17 00:00:00 2001
From: Aaron <69273634+aaron-congo@users.noreply.github.com>
Date: Thu, 13 Nov 2025 08:10:19 -0800
Subject: [PATCH 14/38] chore: remove unused StorageService method (#1597)
---
.../amazon/jdbc/util/storage/StorageService.java | 5 -----
.../amazon/jdbc/util/storage/StorageServiceImpl.java | 11 -----------
2 files changed, 16 deletions(-)
diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageService.java b/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageService.java
index 35770f691..2a49ead0d 100644
--- a/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageService.java
+++ b/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageService.java
@@ -16,7 +16,6 @@
package software.amazon.jdbc.util.storage;
-import java.util.Map;
import org.checkerframework.checker.nullness.qual.Nullable;
public interface StorageService {
@@ -91,9 +90,5 @@ void registerItemClassIfAbsent(
*/
void clearAll();
- // TODO: this is only called by the suggestedClusterId logic in RdsHostListProvider, which will be removed. This
- // method should potentially be removed at that point as well.
- @Nullable Map getEntries(Class itemClass);
-
int size(Class> itemClass);
}
diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageServiceImpl.java b/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageServiceImpl.java
index e59e8d72e..418885ede 100644
--- a/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageServiceImpl.java
+++ b/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageServiceImpl.java
@@ -170,17 +170,6 @@ public void clearAll() {
}
}
- @Override
- public @Nullable Map getEntries(Class itemClass) {
- final ExpirationCache, ?> cache = caches.get(itemClass);
- if (cache == null) {
- return null;
- }
-
- // TODO: remove this method after removing the suggestedClusterId logic
- return (Map) cache.getEntries();
- }
-
@Override
public int size(Class> itemClass) {
final ExpirationCache, ?> cache = caches.get(itemClass);
From b83c3d6902892c4b70dd2f0b7720f0df60ed0ecc Mon Sep 17 00:00:00 2001
From: Aaron <69273634+aaron-congo@users.noreply.github.com>
Date: Fri, 14 Nov 2025 08:33:20 -0800
Subject: [PATCH 15/38] chore: remove deprecated ConnectionService classes
(#1598)
---
...oraGlobalDbMonitoringHostListProvider.java | 3 -
.../util/connection/ConnectionService.java | 58 ---------
.../connection/ConnectionServiceImpl.java | 115 ------------------
.../FailoverConnectionPluginTest.java | 2 -
4 files changed, 178 deletions(-)
delete mode 100644 wrapper/src/main/java/software/amazon/jdbc/util/connection/ConnectionService.java
diff --git a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/AuroraGlobalDbMonitoringHostListProvider.java b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/AuroraGlobalDbMonitoringHostListProvider.java
index 50bb4263d..9053e93db 100644
--- a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/AuroraGlobalDbMonitoringHostListProvider.java
+++ b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/AuroraGlobalDbMonitoringHostListProvider.java
@@ -23,10 +23,8 @@
import java.util.Properties;
import java.util.logging.Logger;
import java.util.stream.Collectors;
-import software.amazon.jdbc.HostListProviderService;
import software.amazon.jdbc.HostSpec;
import software.amazon.jdbc.HostSpecBuilder;
-import software.amazon.jdbc.PluginService;
import software.amazon.jdbc.PropertyDefinition;
import software.amazon.jdbc.hostlistprovider.AuroraGlobalDbHostListProvider;
import software.amazon.jdbc.util.ConnectionUrlParser;
@@ -34,7 +32,6 @@
import software.amazon.jdbc.util.Pair;
import software.amazon.jdbc.util.RdsUtils;
import software.amazon.jdbc.util.StringUtils;
-import software.amazon.jdbc.util.connection.ConnectionService;
public class AuroraGlobalDbMonitoringHostListProvider extends MonitoringRdsHostListProvider {
diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/connection/ConnectionService.java b/wrapper/src/main/java/software/amazon/jdbc/util/connection/ConnectionService.java
deleted file mode 100644
index 1c18a9f28..000000000
--- a/wrapper/src/main/java/software/amazon/jdbc/util/connection/ConnectionService.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License").
- * You may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package software.amazon.jdbc.util.connection;
-
-import java.sql.Connection;
-import java.sql.SQLException;
-import java.util.Properties;
-import software.amazon.jdbc.HostSpec;
-import software.amazon.jdbc.PluginService;
-import software.amazon.jdbc.util.FullServicesContainer;
-
-/**
- * A service used to open new connections for internal driver use.
- *
- * @deprecated This interface is deprecated and will be removed in a future version. Use
- * {@link software.amazon.jdbc.util.ServiceUtility#createMinimalServiceContainer} followed by
- * {@link PluginService#forceConnect} instead.
- */
-@Deprecated
-public interface ConnectionService {
- /**
- * Creates an auxiliary connection. Auxiliary connections are driver-internal connections that accomplish various
- * specific tasks such as monitoring a host's availability, checking the topology information for a cluster, etc.
- *
- * @param hostSpec the hostSpec containing the host information for the auxiliary connection.
- * @param props the properties for the auxiliary connection.
- * @return a new connection to the given host using the given props.
- * @throws SQLException if an error occurs while opening the connection.
- * @deprecated Use {@link software.amazon.jdbc.util.ServiceUtility#createMinimalServiceContainer} followed by
- * {@link PluginService#forceConnect} instead.
- */
- @Deprecated
- Connection open(HostSpec hostSpec, Properties props) throws SQLException;
-
- /**
- * Get the {@link PluginService} associated with this {@link ConnectionService}.
- *
- * @return the {@link PluginService} associated with this {@link ConnectionService}
- * @deprecated Use {@link software.amazon.jdbc.util.ServiceUtility#createMinimalServiceContainer} followed by
- * {@link FullServicesContainer#getPluginService()} instead.
- */
- @Deprecated
- PluginService getPluginService();
-}
diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/connection/ConnectionServiceImpl.java b/wrapper/src/main/java/software/amazon/jdbc/util/connection/ConnectionServiceImpl.java
index 9a9276692..e69de29bb 100644
--- a/wrapper/src/main/java/software/amazon/jdbc/util/connection/ConnectionServiceImpl.java
+++ b/wrapper/src/main/java/software/amazon/jdbc/util/connection/ConnectionServiceImpl.java
@@ -1,115 +0,0 @@
-/*
- * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License").
- * You may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package software.amazon.jdbc.util.connection;
-
-import java.sql.Connection;
-import java.sql.SQLException;
-import java.util.Properties;
-import software.amazon.jdbc.ConnectionPluginManager;
-import software.amazon.jdbc.ConnectionProvider;
-import software.amazon.jdbc.HostSpec;
-import software.amazon.jdbc.PartialPluginService;
-import software.amazon.jdbc.PluginService;
-import software.amazon.jdbc.dialect.Dialect;
-import software.amazon.jdbc.targetdriverdialect.TargetDriverDialect;
-import software.amazon.jdbc.util.FullServicesContainer;
-import software.amazon.jdbc.util.FullServicesContainerImpl;
-import software.amazon.jdbc.util.PropertyUtils;
-import software.amazon.jdbc.util.events.EventPublisher;
-import software.amazon.jdbc.util.monitoring.MonitorService;
-import software.amazon.jdbc.util.storage.StorageService;
-import software.amazon.jdbc.util.telemetry.TelemetryFactory;
-
-/**
- * A service used to open new connections for internal driver use.
- *
- * @deprecated This class is deprecated and will be removed in a future version. Use
- * {@link software.amazon.jdbc.util.ServiceUtility#createMinimalServiceContainer} followed by
- * {@link PluginService#forceConnect} instead.
- */
-@Deprecated
-public class ConnectionServiceImpl implements ConnectionService {
- protected final String targetDriverProtocol;
- protected final ConnectionPluginManager pluginManager;
- protected final PluginService pluginService;
-
- /**
- * Constructs a {@link ConnectionServiceImpl} instance.
- *
- * @param storageService An instance of storage service
- * @param monitorService An instance of monitor service
- * @param telemetryFactory An instance of telemetry factory
- * @param connectionProvider An instance of connection provider
- * @param originalUrl An original Url
- * @param targetDriverProtocol A target driver protocol
- * @param driverDialect An instance of driver dialect
- * @param dbDialect An instance of database dialect
- * @param props Properties
- * @throws SQLException if errors occurred while creating an instance
- * @deprecated Use {@link software.amazon.jdbc.util.ServiceUtility#createMinimalServiceContainer} instead.
- */
- @Deprecated
- public ConnectionServiceImpl(
- StorageService storageService,
- MonitorService monitorService,
- EventPublisher eventPublisher,
- TelemetryFactory telemetryFactory,
- ConnectionProvider connectionProvider,
- String originalUrl,
- String targetDriverProtocol,
- TargetDriverDialect driverDialect,
- Dialect dbDialect,
- Properties props) throws SQLException {
- this.targetDriverProtocol = targetDriverProtocol;
-
- FullServicesContainer servicesContainer =
- new FullServicesContainerImpl(
- storageService, monitorService, eventPublisher, connectionProvider, telemetryFactory);
- this.pluginManager = new ConnectionPluginManager(
- props, telemetryFactory, connectionProvider, null);
- servicesContainer.setConnectionPluginManager(this.pluginManager);
-
- Properties propsCopy = PropertyUtils.copyProperties(props);
- PartialPluginService partialPluginService = new PartialPluginService(
- servicesContainer,
- propsCopy,
- originalUrl,
- this.targetDriverProtocol,
- driverDialect,
- dbDialect
- );
-
- servicesContainer.setHostListProviderService(partialPluginService);
- servicesContainer.setPluginService(partialPluginService);
- servicesContainer.setPluginManagerService(partialPluginService);
-
- this.pluginService = partialPluginService;
- this.pluginManager.initPlugins(servicesContainer, null);
- }
-
- @Override
- @Deprecated
- public Connection open(HostSpec hostSpec, Properties props) throws SQLException {
- return this.pluginManager.forceConnect(this.targetDriverProtocol, hostSpec, props, true, null);
- }
-
- @Override
- @Deprecated
- public PluginService getPluginService() {
- return this.pluginService;
- }
-}
diff --git a/wrapper/src/test/java/software/amazon/jdbc/plugin/failover/FailoverConnectionPluginTest.java b/wrapper/src/test/java/software/amazon/jdbc/plugin/failover/FailoverConnectionPluginTest.java
index 1fb98265e..ac5b9d7b0 100644
--- a/wrapper/src/test/java/software/amazon/jdbc/plugin/failover/FailoverConnectionPluginTest.java
+++ b/wrapper/src/test/java/software/amazon/jdbc/plugin/failover/FailoverConnectionPluginTest.java
@@ -63,7 +63,6 @@
import software.amazon.jdbc.util.FullServicesContainer;
import software.amazon.jdbc.util.RdsUrlType;
import software.amazon.jdbc.util.SqlState;
-import software.amazon.jdbc.util.connection.ConnectionService;
import software.amazon.jdbc.util.telemetry.GaugeCallable;
import software.amazon.jdbc.util.telemetry.TelemetryContext;
import software.amazon.jdbc.util.telemetry.TelemetryCounter;
@@ -82,7 +81,6 @@ class FailoverConnectionPluginTest {
.host("reader1").port(1234).role(HostRole.READER).build());
@Mock FullServicesContainer mockContainer;
- @Mock ConnectionService mockConnectionService;
@Mock PluginService mockPluginService;
@Mock Connection mockConnection;
@Mock HostSpec mockHostSpec;
From d159d41cfc7ddd6374ea9977e3c60bcc6500b59f Mon Sep 17 00:00:00 2001
From: sergiyvamz <75754709+sergiyvamz@users.noreply.github.com>
Date: Wed, 19 Nov 2025 16:37:38 -0800
Subject: [PATCH 16/38] chore: logging improvement for RW Splitting plugin
(#1604)
---
.../ReadWriteSplittingPlugin.java | 24 +++++++++----------
1 file changed, 12 insertions(+), 12 deletions(-)
diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/readwritesplitting/ReadWriteSplittingPlugin.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/readwritesplitting/ReadWriteSplittingPlugin.java
index 5975936c0..587989838 100644
--- a/wrapper/src/main/java/software/amazon/jdbc/plugin/readwritesplitting/ReadWriteSplittingPlugin.java
+++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/readwritesplitting/ReadWriteSplittingPlugin.java
@@ -280,7 +280,7 @@ private void setWriterConnection(final Connection writerConnection,
() -> Messages.get(
"ReadWriteSplittingPlugin.setWriterConnection",
new Object[] {
- writerHostSpec.getUrl()}));
+ writerHostSpec.getHostAndPort()}));
}
private void setReaderConnection(final Connection conn, final HostSpec host) {
@@ -290,7 +290,7 @@ private void setReaderConnection(final Connection conn, final HostSpec host) {
() -> Messages.get(
"ReadWriteSplittingPlugin.setReaderConnection",
new Object[] {
- host.getUrl()}));
+ host.getHostAndPort()}));
}
void switchConnectionIfRequired(final boolean readOnly) throws SQLException {
@@ -332,7 +332,7 @@ void switchConnectionIfRequired(final boolean readOnly) throws SQLException {
"ReadWriteSplittingPlugin.fallbackToWriter",
new Object[] {
e.getMessage(),
- this.pluginService.getCurrentHostSpec().getUrl()}));
+ this.pluginService.getCurrentHostSpec().getHostAndPort()}));
}
}
} else {
@@ -393,7 +393,7 @@ private void switchToWriterConnection(
}
LOGGER.finer(() -> Messages.get("ReadWriteSplittingPlugin.switchedFromReaderToWriter",
- new Object[] {writerHost.getUrl()}));
+ new Object[] {writerHost.getHostAndPort()}));
}
private void switchCurrentConnectionTo(
@@ -409,7 +409,7 @@ private void switchCurrentConnectionTo(
LOGGER.finest(() -> Messages.get(
"ReadWriteSplittingPlugin.settingCurrentConnection",
new Object[] {
- newConnectionHost.getUrl()}));
+ newConnectionHost.getHostAndPort()}));
}
private void switchToReaderConnection(final List hosts)
@@ -436,15 +436,15 @@ private void switchToReaderConnection(final List hosts)
try {
switchCurrentConnectionTo(this.readerConnection, this.readerHostSpec);
LOGGER.finer(() -> Messages.get("ReadWriteSplittingPlugin.switchedFromWriterToReader",
- new Object[] {this.readerHostSpec.getUrl()}));
+ new Object[] {this.readerHostSpec.getHostAndPort()}));
} catch (SQLException e) {
if (e.getMessage() != null) {
LOGGER.warning(
() -> Messages.get("ReadWriteSplittingPlugin.errorSwitchingToCachedReaderWithCause",
- new Object[] {this.readerHostSpec.getUrl(), e.getMessage()}));
+ new Object[] {this.readerHostSpec.getHostAndPort(), e.getMessage()}));
} else {
LOGGER.warning(() -> Messages.get("ReadWriteSplittingPlugin.errorSwitchingToCachedReader",
- new Object[] {this.readerHostSpec.getUrl()}));
+ new Object[] {this.readerHostSpec.getHostAndPort()}));
}
this.readerConnection.close();
@@ -466,11 +466,11 @@ private void initializeReaderConnection(final @NonNull List hosts) thr
getNewWriterConnection(writerHost);
}
LOGGER.warning(() -> Messages.get("ReadWriteSplittingPlugin.noReadersFound",
- new Object[] {writerHost.getUrl()}));
+ new Object[] {writerHost.getHostAndPort()}));
} else {
getNewReaderConnection();
LOGGER.finer(() -> Messages.get("ReadWriteSplittingPlugin.switchedFromWriterToReader",
- new Object[] {this.readerHostSpec.getUrl()}));
+ new Object[] {this.readerHostSpec.getHostAndPort()}));
}
}
@@ -501,7 +501,7 @@ private void getNewReaderConnection() throws SQLException {
Messages.get(
"ReadWriteSplittingPlugin.failedToConnectToReader",
new Object[]{
- hostSpec.getUrl()}),
+ hostSpec.getHostAndPort()}),
e);
}
}
@@ -516,7 +516,7 @@ private void getNewReaderConnection() throws SQLException {
final HostSpec finalReaderHost = readerHost;
LOGGER.finest(
() -> Messages.get("ReadWriteSplittingPlugin.successfullyConnectedToReader",
- new Object[] {finalReaderHost.getUrl()}));
+ new Object[] {finalReaderHost.getHostAndPort()}));
setReaderConnection(conn, readerHost);
switchCurrentConnectionTo(this.readerConnection, this.readerHostSpec);
}
From e59950e1fe32dc3768ac4f5f335c772b58fca31e Mon Sep 17 00:00:00 2001
From: sergiyvamz <75754709+sergiyvamz@users.noreply.github.com>
Date: Wed, 19 Nov 2025 16:45:07 -0800
Subject: [PATCH 17/38] feat: allow to override IAM token property name (#1603)
---
.../UsingTheIamAuthenticationPlugin.md | 13 +++++++------
.../jdbc/plugin/iam/IamAuthConnectionPlugin.java | 15 ++++++++++++---
2 files changed, 19 insertions(+), 9 deletions(-)
diff --git a/docs/using-the-jdbc-driver/using-plugins/UsingTheIamAuthenticationPlugin.md b/docs/using-the-jdbc-driver/using-plugins/UsingTheIamAuthenticationPlugin.md
index ee56dbc60..995cbdc33 100644
--- a/docs/using-the-jdbc-driver/using-plugins/UsingTheIamAuthenticationPlugin.md
+++ b/docs/using-the-jdbc-driver/using-plugins/UsingTheIamAuthenticationPlugin.md
@@ -42,12 +42,13 @@ IAM database authentication use is limited to certain database engines. For more
GRANT rds_iam TO db_userx;`
4. Add the plugin code `iam` to the [`wrapperPlugins`](../UsingTheJdbcDriver.md#connection-plugin-manager-parameters) parameter value.
-| Parameter | Value | Required | Description | Example Value |
-|-------------------|:-------:|:--------:|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------|
-| `iamDefaultPort` | String | No | This property will override the default port that is used to generate the IAM token. The default port is determined based on the underlying driver protocol. For now, there is support for `jdbc:postgresql:` and `jdbc:mysql:`. Target drivers with different protocols will require users to provide a default port. | `1234` |
-| `iamHost` | String | No | This property will override the default hostname that is used to generate the IAM token. The default hostname is derived from the connection string. This parameter is required when users are connecting with custom endpoints. | `database.cluster-hash.us-east-1.rds.amazonaws.com` |
-| `iamRegion` | String | No | This property will override the default region that is used to generate the IAM token. The default region is parsed from the connection string. | `us-east-2` |
-| `iamExpiration` | Integer | No | This property determines how long an IAM token is kept in the driver cache before a new one is generated. The default expiration time is set to be 14 minutes and 30 seconds. Note that IAM database authentication tokens have a lifetime of 15 minutes. | `600` |
+| Parameter | Value | Required | Description | Example Value |
+|------------------------------|:-------:|:--------:|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------|
+| `iamDefaultPort` | String | No | This property will override the default port that is used to generate the IAM token. The default port is determined based on the underlying driver protocol. For now, there is support for `jdbc:postgresql:` and `jdbc:mysql:`. Target drivers with different protocols will require users to provide a default port. | `1234` |
+| `iamHost` | String | No | This property will override the default hostname that is used to generate the IAM token. The default hostname is derived from the connection string. This parameter is required when users are connecting with custom endpoints. | `database.cluster-hash.us-east-1.rds.amazonaws.com` |
+| `iamRegion` | String | No | This property will override the default region that is used to generate the IAM token. The default region is parsed from the connection string. | `us-east-2` |
+| `iamExpiration` | Integer | No | This property determines how long an IAM token is kept in the driver cache before a new one is generated. The default expiration time is set to be 14 minutes and 30 seconds. Note that IAM database authentication tokens have a lifetime of 15 minutes. | `600` |
+| `iamAccessTokenPropertyName` | String | No | This property allows you to override the property name used for passing the IAM access token. Some underlying drivers may require a specific property name for IAM authentication. Default value is `password`. | `password`, `accessToken` |
## Sample code
[AwsIamAuthenticationPostgresqlExample.java](../../../examples/AWSDriverExample/src/main/java/software/amazon/AwsIamAuthenticationPostgresqlExample.java)
diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/iam/IamAuthConnectionPlugin.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/iam/IamAuthConnectionPlugin.java
index 5541ac917..f8a2be272 100644
--- a/wrapper/src/main/java/software/amazon/jdbc/plugin/iam/IamAuthConnectionPlugin.java
+++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/iam/IamAuthConnectionPlugin.java
@@ -72,6 +72,10 @@ public class IamAuthConnectionPlugin extends AbstractConnectionPlugin {
"iamExpiration", String.valueOf(DEFAULT_TOKEN_EXPIRATION_SEC),
"IAM token cache expiration in seconds");
+ public static final AwsWrapperProperty IAM_TOKEN_PROPERTY_NAME = new AwsWrapperProperty(
+ "iamAccessTokenPropertyName", PropertyDefinition.PASSWORD.name,
+ "Overrides default IAM access token property name");
+
protected static final RegionUtils regionUtils = new RegionUtils();
protected final PluginService pluginService;
protected final RdsUtils rdsUtils = new RdsUtils();
@@ -121,6 +125,10 @@ private Connection connectInternal(String driverProtocol, HostSpec hostSpec, Pro
throw new SQLException(PropertyDefinition.USER.name + " is null or empty.");
}
+ if (StringUtils.isNullOrEmpty(IAM_TOKEN_PROPERTY_NAME.getString(props))) {
+ throw new SQLException(IAM_TOKEN_PROPERTY_NAME.name + " is null or empty.");
+ }
+
String host = IamAuthUtils.getIamHost(IAM_HOST.getString(props), hostSpec);
int port = IamAuthUtils.getIamPort(
@@ -149,7 +157,7 @@ private Connection connectInternal(String driverProtocol, HostSpec hostSpec, Pro
() -> Messages.get(
"AuthenticationToken.useCachedToken",
new Object[] {tokenInfo.getToken()}));
- PropertyDefinition.PASSWORD.set(props, tokenInfo.getToken());
+ props.setProperty(IAM_TOKEN_PROPERTY_NAME.getString(props), tokenInfo.getToken());
} else {
final Instant tokenExpiry = Instant.now().plus(tokenExpirationSec, ChronoUnit.SECONDS);
if (this.fetchTokenCounter != null) {
@@ -167,7 +175,8 @@ private Connection connectInternal(String driverProtocol, HostSpec hostSpec, Pro
() -> Messages.get(
"AuthenticationToken.generatedNewToken",
new Object[] {token}));
- PropertyDefinition.PASSWORD.set(props, token);
+
+ props.setProperty(IAM_TOKEN_PROPERTY_NAME.getString(props), token);
IamAuthCacheHolder.tokenCache.put(
cacheKey,
new TokenInfo(token, tokenExpiry));
@@ -206,7 +215,7 @@ private Connection connectInternal(String driverProtocol, HostSpec hostSpec, Pro
() -> Messages.get(
"AuthenticationToken.generatedNewToken",
new Object[] {token}));
- PropertyDefinition.PASSWORD.set(props, token);
+ props.setProperty(IAM_TOKEN_PROPERTY_NAME.getString(props), token);
IamAuthCacheHolder.tokenCache.put(
cacheKey,
new TokenInfo(token, tokenExpiry));
From 923b61bc78168bb338ca8f0751967343184402ac Mon Sep 17 00:00:00 2001
From: Aaron <69273634+aaron-congo@users.noreply.github.com>
Date: Thu, 20 Nov 2025 09:41:16 -0800
Subject: [PATCH 18/38] Refactor: dialects and host list providers to reduce
code duplication (#1588)
---
.../ConnectionPluginManagerBenchmarks.java | 2 +-
.../jdbc/benchmarks/PluginBenchmarks.java | 2 +-
.../testplugin/BenchmarkPlugin.java | 2 +-
.../testplugin/TestConnectionWrapper.java | 2 +-
.../amazon/jdbc/BlockingHostListProvider.java | 1 +
.../amazon/jdbc/ConnectionPlugin.java | 1 +
.../amazon/jdbc/ConnectionPluginManager.java | 1 +
.../amazon/jdbc/PartialPluginService.java | 7 +-
.../software/amazon/jdbc/PluginService.java | 1 +
.../amazon/jdbc/PluginServiceImpl.java | 7 +-
.../jdbc/dialect/AuroraMysqlDialect.java | 116 ++---
.../amazon/jdbc/dialect/AuroraPgDialect.java | 176 +++-----
.../amazon/jdbc/dialect/BlueGreenDialect.java | 4 +-
.../software/amazon/jdbc/dialect/Dialect.java | 15 +-
.../amazon/jdbc/dialect/DialectManager.java | 23 +-
.../amazon/jdbc/dialect/DialectUtils.java | 46 ++
.../dialect/GlobalAuroraMysqlDialect.java | 107 ++---
.../jdbc/dialect/GlobalAuroraPgDialect.java | 123 ++----
.../dialect/GlobalAuroraTopologyDialect.java | 21 +
.../dialect/HostListProviderSupplier.java | 2 +-
.../amazon/jdbc/dialect/MariaDbDialect.java | 85 ++--
.../jdbc/dialect/MultiAzClusterDialect.java} | 14 +-
...t.java => MultiAzClusterMysqlDialect.java} | 117 +++--
...lect.java => MultiAzClusterPgDialect.java} | 116 ++---
.../amazon/jdbc/dialect/MysqlDialect.java | 83 ++--
.../amazon/jdbc/dialect/PgDialect.java | 80 ++--
.../amazon/jdbc/dialect/RdsMysqlDialect.java | 77 ++--
.../amazon/jdbc/dialect/RdsPgDialect.java | 63 +--
.../amazon/jdbc/dialect/TopologyDialect.java | 27 ++
.../amazon/jdbc/dialect/UnknownDialect.java | 2 +-
.../AuroraGlobalDbHostListProvider.java | 114 -----
.../AuroraHostListProvider.java | 43 --
.../hostlistprovider/AuroraTopologyUtils.java | 95 ++++
.../ConnectionStringHostListProvider.java | 5 +-
.../DynamicHostListProvider.java | 8 +-
.../GlobalAuroraHostListProvider.java | 73 ++++
.../GlobalAuroraTopologyUtils.java | 152 +++++++
.../HostListProvider.java | 6 +-
.../HostListProviderService.java | 4 +-
.../MultiAzTopologyUtils.java | 117 +++++
.../hostlistprovider/RdsHostListProvider.java | 304 +++----------
.../RdsMultiAzDbClusterListProvider.java | 208 ---------
.../StaticHostListProvider.java | 6 +-
.../jdbc/hostlistprovider/TopologyUtils.java | 235 ++++++++++
...oraGlobalDbMonitoringHostListProvider.java | 108 -----
.../ClusterTopologyMonitorImpl.java | 412 +++++-------------
.../GlobalAuroraTopologyMonitor.java | 78 ++++
.../GlobalDbClusterTopologyMonitorImpl.java | 111 -----
...onitoringGlobalAuroraHostListProvider.java | 91 ++++
.../MonitoringRdsHostListProvider.java | 39 +-
.../MonitoringRdsMultiAzHostListProvider.java | 74 ----
.../MultiAzClusterTopologyMonitorImpl.java | 128 ------
.../jdbc/plugin/AbstractConnectionPlugin.java | 2 +-
...AuroraInitialConnectionStrategyPlugin.java | 2 +-
.../jdbc/plugin/DefaultConnectionPlugin.java | 2 +-
.../bluegreen/BlueGreenInterimStatus.java | 6 +-
.../bluegreen/BlueGreenStatusMonitor.java | 4 +-
.../ClusterAwareReaderFailoverHandler.java | 4 +-
.../ClusterAwareWriterFailoverHandler.java | 3 +-
.../failover/FailoverConnectionPlugin.java | 7 +-
.../failover2/FailoverConnectionPlugin.java | 11 +-
.../limitless/LimitlessRouterMonitor.java | 4 +-
.../ReadWriteSplittingPlugin.java | 5 +-
.../plugin/staledns/AuroraStaleDnsHelper.java | 7 +-
.../plugin/staledns/AuroraStaleDnsPlugin.java | 2 +-
.../HostResponseTimeServiceImpl.java | 1 -
.../jdbc/util/FullServicesContainer.java | 2 +-
.../jdbc/util/FullServicesContainerImpl.java | 2 +-
.../software/amazon/jdbc/util/LogUtils.java | 55 +++
.../amazon/jdbc/util/ServiceUtility.java | 4 +-
.../java/software/amazon/jdbc/util/Utils.java | 24 -
.../util/monitoring/MonitorServiceImpl.java | 2 -
.../jdbc/wrapper/ConnectionWrapper.java | 2 +-
..._advanced_jdbc_wrapper_messages.properties | 37 +-
.../tests/AdvancedPerformanceTest.java | 2 +-
.../container/tests/AutoscalingTests.java | 6 +-
.../container/tests/FailoverTest.java | 4 +-
.../container/tests/PerformanceTest.java | 1 -
.../tests/ReadWriteSplittingTests.java | 6 +-
.../amazon/jdbc/DialectDetectionTests.java | 13 +-
.../software/amazon/jdbc/DialectTests.java | 8 +-
.../amazon/jdbc/PluginServiceImplTests.java | 1 +
.../jdbc/RoundRobinHostSelectorTest.java | 5 -
.../RdsHostListProviderTest.java | 227 +---------
.../RdsMultiAzDbClusterListProviderTest.java | 311 -------------
.../amazon/jdbc/mock/TestPluginOne.java | 2 +-
.../FailoverConnectionPluginTest.java | 6 +-
.../LimitlessConnectionPluginTest.java | 2 +-
.../LimitlessRouterServiceImplTest.java | 2 +-
.../ReadWriteSplittingPluginTest.java | 2 +-
.../hibernate_files/DataSourceTest.java | 7 +-
...stgreSQLCastingIntervalSecondJdbcType.java | 1 -
.../PostgresIntervalSecondTest.java | 19 +-
.../StructEmbeddableArrayTest.java | 20 +-
94 files changed, 1806 insertions(+), 2761 deletions(-)
create mode 100644 wrapper/src/main/java/software/amazon/jdbc/dialect/DialectUtils.java
create mode 100644 wrapper/src/main/java/software/amazon/jdbc/dialect/GlobalAuroraTopologyDialect.java
rename wrapper/src/{test/java/integration/container/aurora/TestAuroraHostListProvider.java => main/java/software/amazon/jdbc/dialect/MultiAzClusterDialect.java} (57%)
rename wrapper/src/main/java/software/amazon/jdbc/dialect/{RdsMultiAzDbClusterMysqlDialect.java => MultiAzClusterMysqlDialect.java} (52%)
rename wrapper/src/main/java/software/amazon/jdbc/dialect/{RdsMultiAzDbClusterPgDialect.java => MultiAzClusterPgDialect.java} (59%)
create mode 100644 wrapper/src/main/java/software/amazon/jdbc/dialect/TopologyDialect.java
delete mode 100644 wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/AuroraGlobalDbHostListProvider.java
delete mode 100644 wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/AuroraHostListProvider.java
create mode 100644 wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/AuroraTopologyUtils.java
create mode 100644 wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/GlobalAuroraHostListProvider.java
create mode 100644 wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/GlobalAuroraTopologyUtils.java
rename wrapper/src/main/java/software/amazon/jdbc/{ => hostlistprovider}/HostListProvider.java (89%)
rename wrapper/src/main/java/software/amazon/jdbc/{ => hostlistprovider}/HostListProviderService.java (89%)
create mode 100644 wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/MultiAzTopologyUtils.java
delete mode 100644 wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/RdsMultiAzDbClusterListProvider.java
create mode 100644 wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/TopologyUtils.java
delete mode 100644 wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/AuroraGlobalDbMonitoringHostListProvider.java
create mode 100644 wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/GlobalAuroraTopologyMonitor.java
delete mode 100644 wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/GlobalDbClusterTopologyMonitorImpl.java
create mode 100644 wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MonitoringGlobalAuroraHostListProvider.java
delete mode 100644 wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MonitoringRdsMultiAzHostListProvider.java
delete mode 100644 wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MultiAzClusterTopologyMonitorImpl.java
create mode 100644 wrapper/src/main/java/software/amazon/jdbc/util/LogUtils.java
delete mode 100644 wrapper/src/test/java/software/amazon/jdbc/hostlistprovider/RdsMultiAzDbClusterListProviderTest.java
diff --git a/benchmarks/src/jmh/java/software/amazon/jdbc/benchmarks/ConnectionPluginManagerBenchmarks.java b/benchmarks/src/jmh/java/software/amazon/jdbc/benchmarks/ConnectionPluginManagerBenchmarks.java
index eb4a8d3b2..ebdb34355 100644
--- a/benchmarks/src/jmh/java/software/amazon/jdbc/benchmarks/ConnectionPluginManagerBenchmarks.java
+++ b/benchmarks/src/jmh/java/software/amazon/jdbc/benchmarks/ConnectionPluginManagerBenchmarks.java
@@ -54,7 +54,7 @@
import software.amazon.jdbc.ConnectionPluginFactory;
import software.amazon.jdbc.ConnectionPluginManager;
import software.amazon.jdbc.ConnectionProvider;
-import software.amazon.jdbc.HostListProviderService;
+import software.amazon.jdbc.hostlistprovider.HostListProviderService;
import software.amazon.jdbc.HostSpec;
import software.amazon.jdbc.HostSpecBuilder;
import software.amazon.jdbc.JdbcMethod;
diff --git a/benchmarks/src/jmh/java/software/amazon/jdbc/benchmarks/PluginBenchmarks.java b/benchmarks/src/jmh/java/software/amazon/jdbc/benchmarks/PluginBenchmarks.java
index 405b6fc00..dda847f73 100644
--- a/benchmarks/src/jmh/java/software/amazon/jdbc/benchmarks/PluginBenchmarks.java
+++ b/benchmarks/src/jmh/java/software/amazon/jdbc/benchmarks/PluginBenchmarks.java
@@ -54,7 +54,7 @@
import software.amazon.jdbc.ConnectionProviderManager;
import software.amazon.jdbc.Driver;
import software.amazon.jdbc.HikariPooledConnectionProvider;
-import software.amazon.jdbc.HostListProviderService;
+import software.amazon.jdbc.hostlistprovider.HostListProviderService;
import software.amazon.jdbc.HostSpec;
import software.amazon.jdbc.HostSpecBuilder;
import software.amazon.jdbc.JdbcMethod;
diff --git a/benchmarks/src/jmh/java/software/amazon/jdbc/benchmarks/testplugin/BenchmarkPlugin.java b/benchmarks/src/jmh/java/software/amazon/jdbc/benchmarks/testplugin/BenchmarkPlugin.java
index ffed10f77..07fede27a 100644
--- a/benchmarks/src/jmh/java/software/amazon/jdbc/benchmarks/testplugin/BenchmarkPlugin.java
+++ b/benchmarks/src/jmh/java/software/amazon/jdbc/benchmarks/testplugin/BenchmarkPlugin.java
@@ -28,7 +28,7 @@
import java.util.Set;
import java.util.logging.Logger;
import software.amazon.jdbc.ConnectionPlugin;
-import software.amazon.jdbc.HostListProviderService;
+import software.amazon.jdbc.hostlistprovider.HostListProviderService;
import software.amazon.jdbc.HostRole;
import software.amazon.jdbc.HostSpec;
import software.amazon.jdbc.HostSpecBuilder;
diff --git a/benchmarks/src/jmh/java/software/amazon/jdbc/benchmarks/testplugin/TestConnectionWrapper.java b/benchmarks/src/jmh/java/software/amazon/jdbc/benchmarks/testplugin/TestConnectionWrapper.java
index a3c0cd7f2..483d6768c 100644
--- a/benchmarks/src/jmh/java/software/amazon/jdbc/benchmarks/testplugin/TestConnectionWrapper.java
+++ b/benchmarks/src/jmh/java/software/amazon/jdbc/benchmarks/testplugin/TestConnectionWrapper.java
@@ -20,7 +20,7 @@
import java.util.Properties;
import org.checkerframework.checker.nullness.qual.NonNull;
import software.amazon.jdbc.ConnectionPluginManager;
-import software.amazon.jdbc.HostListProviderService;
+import software.amazon.jdbc.hostlistprovider.HostListProviderService;
import software.amazon.jdbc.PluginManagerService;
import software.amazon.jdbc.PluginService;
import software.amazon.jdbc.wrapper.ConnectionWrapper;
diff --git a/wrapper/src/main/java/software/amazon/jdbc/BlockingHostListProvider.java b/wrapper/src/main/java/software/amazon/jdbc/BlockingHostListProvider.java
index 9fe7e40fb..31f3d1182 100644
--- a/wrapper/src/main/java/software/amazon/jdbc/BlockingHostListProvider.java
+++ b/wrapper/src/main/java/software/amazon/jdbc/BlockingHostListProvider.java
@@ -19,6 +19,7 @@
import java.sql.SQLException;
import java.util.List;
import java.util.concurrent.TimeoutException;
+import software.amazon.jdbc.hostlistprovider.HostListProvider;
public interface BlockingHostListProvider extends HostListProvider {
diff --git a/wrapper/src/main/java/software/amazon/jdbc/ConnectionPlugin.java b/wrapper/src/main/java/software/amazon/jdbc/ConnectionPlugin.java
index d2d72b05c..ad271f2ad 100644
--- a/wrapper/src/main/java/software/amazon/jdbc/ConnectionPlugin.java
+++ b/wrapper/src/main/java/software/amazon/jdbc/ConnectionPlugin.java
@@ -23,6 +23,7 @@
import java.util.Map;
import java.util.Properties;
import java.util.Set;
+import software.amazon.jdbc.hostlistprovider.HostListProviderService;
/**
* Interface for connection plugins. This class implements ways to execute a JDBC method and to clean up resources used
diff --git a/wrapper/src/main/java/software/amazon/jdbc/ConnectionPluginManager.java b/wrapper/src/main/java/software/amazon/jdbc/ConnectionPluginManager.java
index 2697c5b03..33f7618b9 100644
--- a/wrapper/src/main/java/software/amazon/jdbc/ConnectionPluginManager.java
+++ b/wrapper/src/main/java/software/amazon/jdbc/ConnectionPluginManager.java
@@ -30,6 +30,7 @@
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import software.amazon.jdbc.cleanup.CanReleaseResources;
+import software.amazon.jdbc.hostlistprovider.HostListProviderService;
import software.amazon.jdbc.plugin.AuroraConnectionTrackerPlugin;
import software.amazon.jdbc.plugin.AuroraInitialConnectionStrategyPlugin;
import software.amazon.jdbc.plugin.AwsSecretsManagerConnectionPlugin;
diff --git a/wrapper/src/main/java/software/amazon/jdbc/PartialPluginService.java b/wrapper/src/main/java/software/amazon/jdbc/PartialPluginService.java
index 8fc761130..0fd7b6060 100644
--- a/wrapper/src/main/java/software/amazon/jdbc/PartialPluginService.java
+++ b/wrapper/src/main/java/software/amazon/jdbc/PartialPluginService.java
@@ -42,11 +42,14 @@
import software.amazon.jdbc.exceptions.ExceptionManager;
import software.amazon.jdbc.hostavailability.HostAvailability;
import software.amazon.jdbc.hostavailability.HostAvailabilityStrategyFactory;
+import software.amazon.jdbc.hostlistprovider.HostListProvider;
+import software.amazon.jdbc.hostlistprovider.HostListProviderService;
import software.amazon.jdbc.hostlistprovider.StaticHostListProvider;
import software.amazon.jdbc.profile.ConfigurationProfile;
import software.amazon.jdbc.states.SessionStateService;
import software.amazon.jdbc.targetdriverdialect.TargetDriverDialect;
import software.amazon.jdbc.util.FullServicesContainer;
+import software.amazon.jdbc.util.LogUtils;
import software.amazon.jdbc.util.Messages;
import software.amazon.jdbc.util.Utils;
import software.amazon.jdbc.util.storage.CacheMap;
@@ -132,7 +135,7 @@ public PartialPluginService(
? this.configurationProfile.getExceptionHandler()
: null;
- HostListProviderSupplier supplier = this.dbDialect.getHostListProvider();
+ HostListProviderSupplier supplier = this.dbDialect.getHostListProviderSupplier();
this.hostListProvider = supplier.getProvider(this.props, this.originalUrl, this.servicesContainer);
}
@@ -159,7 +162,7 @@ public HostSpec getCurrentHostSpec() {
Messages.get("PluginServiceImpl.currentHostNotAllowed",
new Object[] {
currentHostSpec == null ? "" : currentHostSpec.getHostAndPort(),
- Utils.logTopology(allowedHosts, "")})
+ LogUtils.logTopology(allowedHosts, "")})
);
}
diff --git a/wrapper/src/main/java/software/amazon/jdbc/PluginService.java b/wrapper/src/main/java/software/amazon/jdbc/PluginService.java
index aae57d7f4..bc1e232f2 100644
--- a/wrapper/src/main/java/software/amazon/jdbc/PluginService.java
+++ b/wrapper/src/main/java/software/amazon/jdbc/PluginService.java
@@ -28,6 +28,7 @@
import software.amazon.jdbc.dialect.Dialect;
import software.amazon.jdbc.exceptions.ExceptionHandler;
import software.amazon.jdbc.hostavailability.HostAvailability;
+import software.amazon.jdbc.hostlistprovider.HostListProvider;
import software.amazon.jdbc.states.SessionStateService;
import software.amazon.jdbc.targetdriverdialect.TargetDriverDialect;
import software.amazon.jdbc.util.telemetry.TelemetryFactory;
diff --git a/wrapper/src/main/java/software/amazon/jdbc/PluginServiceImpl.java b/wrapper/src/main/java/software/amazon/jdbc/PluginServiceImpl.java
index 21dd7bb3a..3d0371ae2 100644
--- a/wrapper/src/main/java/software/amazon/jdbc/PluginServiceImpl.java
+++ b/wrapper/src/main/java/software/amazon/jdbc/PluginServiceImpl.java
@@ -47,12 +47,15 @@
import software.amazon.jdbc.exceptions.ExceptionManager;
import software.amazon.jdbc.hostavailability.HostAvailability;
import software.amazon.jdbc.hostavailability.HostAvailabilityStrategyFactory;
+import software.amazon.jdbc.hostlistprovider.HostListProvider;
+import software.amazon.jdbc.hostlistprovider.HostListProviderService;
import software.amazon.jdbc.hostlistprovider.StaticHostListProvider;
import software.amazon.jdbc.profile.ConfigurationProfile;
import software.amazon.jdbc.states.SessionStateService;
import software.amazon.jdbc.states.SessionStateServiceImpl;
import software.amazon.jdbc.targetdriverdialect.TargetDriverDialect;
import software.amazon.jdbc.util.FullServicesContainer;
+import software.amazon.jdbc.util.LogUtils;
import software.amazon.jdbc.util.Messages;
import software.amazon.jdbc.util.Utils;
import software.amazon.jdbc.util.storage.CacheMap;
@@ -192,7 +195,7 @@ public HostSpec getCurrentHostSpec() {
Messages.get("PluginServiceImpl.currentHostNotAllowed",
new Object[] {
currentHostSpec == null ? "" : currentHostSpec.getHostAndPort(),
- Utils.logTopology(allowedHosts, "")})
+ LogUtils.logTopology(allowedHosts, "")})
);
}
@@ -708,7 +711,7 @@ public void updateDialect(final @NonNull Connection connection) throws SQLExcept
return;
}
- final HostListProviderSupplier supplier = this.dialect.getHostListProvider();
+ final HostListProviderSupplier supplier = this.dialect.getHostListProviderSupplier();
this.setHostListProvider(supplier.getProvider(this.props, this.originalUrl, this.servicesContainer));
this.refreshHostList(connection);
}
diff --git a/wrapper/src/main/java/software/amazon/jdbc/dialect/AuroraMysqlDialect.java b/wrapper/src/main/java/software/amazon/jdbc/dialect/AuroraMysqlDialect.java
index 1e44afdb0..8ae253cbd 100644
--- a/wrapper/src/main/java/software/amazon/jdbc/dialect/AuroraMysqlDialect.java
+++ b/wrapper/src/main/java/software/amazon/jdbc/dialect/AuroraMysqlDialect.java
@@ -17,119 +17,85 @@
package software.amazon.jdbc.dialect;
import java.sql.Connection;
-import java.sql.ResultSet;
-import java.sql.SQLException;
-import java.sql.Statement;
-import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import software.amazon.jdbc.PluginService;
-import software.amazon.jdbc.hostlistprovider.AuroraHostListProvider;
+import software.amazon.jdbc.hostlistprovider.AuroraTopologyUtils;
+import software.amazon.jdbc.hostlistprovider.RdsHostListProvider;
+import software.amazon.jdbc.hostlistprovider.TopologyUtils;
import software.amazon.jdbc.hostlistprovider.monitoring.MonitoringRdsHostListProvider;
import software.amazon.jdbc.plugin.failover2.FailoverConnectionPlugin;
-public class AuroraMysqlDialect extends MysqlDialect implements BlueGreenDialect {
+public class AuroraMysqlDialect extends MysqlDialect implements TopologyDialect, BlueGreenDialect {
- protected final String topologyQuery =
+ protected static final String AURORA_VERSION_EXISTS_QUERY = "SHOW VARIABLES LIKE 'aurora_version'";
+ protected static final String TOPOLOGY_QUERY =
"SELECT SERVER_ID, CASE WHEN SESSION_ID = 'MASTER_SESSION_ID' THEN TRUE ELSE FALSE END, "
+ "CPU, REPLICA_LAG_IN_MILLISECONDS, LAST_UPDATE_TIMESTAMP "
+ "FROM information_schema.replica_host_status "
- // filter out nodes that haven't been updated in the last 5 minutes
+ // filter out instances that have not been updated in the last 5 minutes
+ "WHERE time_to_sec(timediff(now(), LAST_UPDATE_TIMESTAMP)) <= 300 OR SESSION_ID = 'MASTER_SESSION_ID' ";
- protected final String isWriterQuery =
+ protected static final String INSTANCE_ID_QUERY = "SELECT @@aurora_server_id, @@aurora_server_id";
+ protected static final String WRITER_ID_QUERY =
"SELECT SERVER_ID FROM information_schema.replica_host_status "
- + "WHERE SESSION_ID = 'MASTER_SESSION_ID' AND SERVER_ID = @@aurora_server_id";
+ + "WHERE SESSION_ID = 'MASTER_SESSION_ID' AND SERVER_ID = @@aurora_server_id";
+ protected static final String IS_READER_QUERY = "SELECT @@innodb_read_only";
- protected final String nodeIdQuery = "SELECT @@aurora_server_id, @@aurora_server_id";
- protected final String isReaderQuery = "SELECT @@innodb_read_only";
-
- private static final String BG_STATUS_QUERY =
- "SELECT * FROM mysql.rds_topology";
-
- private static final String TOPOLOGY_TABLE_EXIST_QUERY =
+ protected static final String BG_TOPOLOGY_EXISTS_QUERY =
"SELECT 1 AS tmp FROM information_schema.tables WHERE"
+ " table_schema = 'mysql' AND table_name = 'rds_topology'";
+ protected static final String BG_STATUS_QUERY = "SELECT * FROM mysql.rds_topology";
@Override
public boolean isDialect(final Connection connection) {
- Statement stmt = null;
- ResultSet rs = null;
- try {
- stmt = connection.createStatement();
- rs = stmt.executeQuery("SHOW VARIABLES LIKE 'aurora_version'");
- if (rs.next()) {
- // If variable with such name is presented then it means it's an Aurora cluster
- return true;
- }
- } catch (final SQLException ex) {
- // ignore
- } finally {
- if (stmt != null) {
- try {
- stmt.close();
- } catch (SQLException ex) {
- // ignore
- }
- }
- if (rs != null) {
- try {
- rs.close();
- } catch (SQLException ex) {
- // ignore
- }
- }
- }
- return false;
+ return dialectUtils.checkExistenceQueries(connection, AURORA_VERSION_EXISTS_QUERY);
}
@Override
public List* dialect code */ String> getDialectUpdateCandidates() {
- return Arrays.asList(
- DialectCodes.GLOBAL_AURORA_MYSQL,
- DialectCodes.RDS_MULTI_AZ_MYSQL_CLUSTER);
+ return Collections.singletonList(DialectCodes.RDS_MULTI_AZ_MYSQL_CLUSTER);
}
@Override
- public HostListProviderSupplier getHostListProvider() {
+ public HostListProviderSupplier getHostListProviderSupplier() {
return (properties, initialUrl, servicesContainer) -> {
final PluginService pluginService = servicesContainer.getPluginService();
+ final TopologyUtils topologyUtils = new AuroraTopologyUtils(this, pluginService.getHostSpecBuilder());
if (pluginService.isPluginInUse(FailoverConnectionPlugin.class)) {
- return new MonitoringRdsHostListProvider(
- properties,
- initialUrl,
- servicesContainer,
- this.topologyQuery,
- this.nodeIdQuery,
- this.isReaderQuery,
- this.isWriterQuery);
+ return new MonitoringRdsHostListProvider(topologyUtils, properties, initialUrl, servicesContainer);
}
- return new AuroraHostListProvider(
- properties,
- initialUrl,
- servicesContainer,
- this.topologyQuery,
- this.nodeIdQuery,
- this.isReaderQuery);
+ return new RdsHostListProvider(topologyUtils, properties, initialUrl, servicesContainer);
};
}
@Override
- public String getBlueGreenStatusQuery() {
- return BG_STATUS_QUERY;
+ public String getTopologyQuery() {
+ return TOPOLOGY_QUERY;
+ }
+
+ @Override
+ public String getInstanceIdQuery() {
+ return INSTANCE_ID_QUERY;
+ }
+
+ @Override
+ public String getWriterIdQuery() {
+ return WRITER_ID_QUERY;
+ }
+
+ @Override
+ public String getIsReaderQuery() {
+ return IS_READER_QUERY;
}
@Override
public boolean isBlueGreenStatusAvailable(final Connection connection) {
- try {
- try (Statement statement = connection.createStatement();
- ResultSet rs = statement.executeQuery(TOPOLOGY_TABLE_EXIST_QUERY)) {
- return rs.next();
- }
- } catch (SQLException ex) {
- return false;
- }
+ return dialectUtils.checkExistenceQueries(connection, BG_TOPOLOGY_EXISTS_QUERY);
}
+ @Override
+ public String getBlueGreenStatusQuery() {
+ return BG_STATUS_QUERY;
+ }
}
-
diff --git a/wrapper/src/main/java/software/amazon/jdbc/dialect/AuroraPgDialect.java b/wrapper/src/main/java/software/amazon/jdbc/dialect/AuroraPgDialect.java
index c88f595af..e43f30cb8 100644
--- a/wrapper/src/main/java/software/amazon/jdbc/dialect/AuroraPgDialect.java
+++ b/wrapper/src/main/java/software/amazon/jdbc/dialect/AuroraPgDialect.java
@@ -24,59 +24,49 @@
import java.util.List;
import java.util.logging.Logger;
import software.amazon.jdbc.PluginService;
-import software.amazon.jdbc.hostlistprovider.AuroraHostListProvider;
+import software.amazon.jdbc.hostlistprovider.AuroraTopologyUtils;
+import software.amazon.jdbc.hostlistprovider.RdsHostListProvider;
+import software.amazon.jdbc.hostlistprovider.TopologyUtils;
import software.amazon.jdbc.hostlistprovider.monitoring.MonitoringRdsHostListProvider;
import software.amazon.jdbc.plugin.failover2.FailoverConnectionPlugin;
import software.amazon.jdbc.util.DriverInfo;
+import software.amazon.jdbc.util.Messages;
-/**
- * Suitable for the following AWS PG configurations.
- * - Regional Cluster
- */
-public class AuroraPgDialect extends PgDialect implements AuroraLimitlessDialect, BlueGreenDialect {
- private static final Logger LOGGER = Logger.getLogger(AuroraPgDialect.class.getName());
+public class AuroraPgDialect extends PgDialect implements TopologyDialect, AuroraLimitlessDialect, BlueGreenDialect {
- protected final String extensionsSql =
+ protected static final String AURORA_UTILS_EXIST_QUERY =
"SELECT (setting LIKE '%aurora_stat_utils%') AS aurora_stat_utils "
+ "FROM pg_catalog.pg_settings "
+ "WHERE name OPERATOR(pg_catalog.=) 'rds.extensions'";
-
- protected final String topologySql = "SELECT 1 FROM pg_catalog.aurora_replica_status() LIMIT 1";
-
- protected final String topologyQuery =
+ protected static final String TOPOLOGY_EXISTS_QUERY = "SELECT 1 FROM pg_catalog.aurora_replica_status() LIMIT 1";
+ protected static final String TOPOLOGY_QUERY =
"SELECT SERVER_ID, CASE WHEN SESSION_ID OPERATOR(pg_catalog.=) 'MASTER_SESSION_ID' THEN TRUE ELSE FALSE END, "
+ "CPU, COALESCE(REPLICA_LAG_IN_MSEC, 0), LAST_UPDATE_TIMESTAMP "
+ "FROM pg_catalog.aurora_replica_status() "
- // filter out nodes that haven't been updated in the last 5 minutes
+ // filter out instances that haven't been updated in the last 5 minutes
+ "WHERE EXTRACT("
+ "EPOCH FROM(pg_catalog.NOW() OPERATOR(pg_catalog.-) LAST_UPDATE_TIMESTAMP)) OPERATOR(pg_catalog.<=) 300 "
+ "OR SESSION_ID OPERATOR(pg_catalog.=) 'MASTER_SESSION_ID' "
+ "OR LAST_UPDATE_TIMESTAMP IS NULL";
- protected final String isWriterQuery =
+ protected static final String INSTANCE_ID_QUERY =
+ "SELECT pg_catalog.aurora_db_instance_identifier(), pg_catalog.aurora_db_instance_identifier()";
+ protected static final String WRITER_ID_QUERY =
"SELECT SERVER_ID FROM pg_catalog.aurora_replica_status() "
+ "WHERE SESSION_ID OPERATOR(pg_catalog.=) 'MASTER_SESSION_ID' "
+ "AND SERVER_ID OPERATOR(pg_catalog.=) pg_catalog.aurora_db_instance_identifier()";
+ protected static final String IS_READER_QUERY = "SELECT pg_catalog.pg_is_in_recovery()";
- protected final String nodeIdQuery =
- "SELECT pg_catalog.aurora_db_instance_identifier(), pg_catalog.aurora_db_instance_identifier()";
- protected final String isReaderQuery = "SELECT pg_catalog.pg_is_in_recovery()";
protected static final String LIMITLESS_ROUTER_ENDPOINT_QUERY =
"select router_endpoint, load from pg_catalog.aurora_limitless_router_endpoints()";
- private static final String BG_STATUS_QUERY =
- "SELECT * FROM "
- + "pg_catalog.get_blue_green_fast_switchover_metadata('aws_jdbc_driver-" + DriverInfo.DRIVER_VERSION + "')";
-
- private static final String TOPOLOGY_TABLE_EXIST_QUERY =
+ protected static final String BG_TOPOLOGY_EXISTS_QUERY =
"SELECT 'pg_catalog.get_blue_green_fast_switchover_metadata'::regproc";
+ protected static final String BG_STATUS_QUERY =
+ "SELECT * FROM "
+ + "pg_catalog.get_blue_green_fast_switchover_metadata('aws_jdbc_driver-" + DriverInfo.DRIVER_VERSION + "')";
- @Override
- public List getDialectUpdateCandidates() {
- return Arrays.asList(DialectCodes.GLOBAL_AURORA_PG,
- DialectCodes.RDS_MULTI_AZ_PG_CLUSTER,
- DialectCodes.RDS_PG);
- }
+ private static final Logger LOGGER = Logger.getLogger(AuroraPgDialect.class.getName());
@Override
public boolean isDialect(final Connection connection) {
@@ -84,112 +74,80 @@ public boolean isDialect(final Connection connection) {
return false;
}
- Statement stmt = null;
- ResultSet rs = null;
boolean hasExtensions = false;
- boolean hasTopology = false;
- try {
- stmt = connection.createStatement();
- rs = stmt.executeQuery(extensionsSql);
- if (rs.next()) {
- final boolean auroraUtils = rs.getBoolean("aurora_stat_utils");
- LOGGER.finest(() -> String.format("auroraUtils: %b", auroraUtils));
- if (auroraUtils) {
- hasExtensions = true;
- }
- }
- } catch (SQLException ex) {
- // ignore
- } finally {
- if (stmt != null) {
- try {
- stmt.close();
- } catch (SQLException ex) {
- // ignore
- }
+ try (Statement stmt = connection.createStatement();
+ ResultSet rs = stmt.executeQuery(AURORA_UTILS_EXIST_QUERY)) {
+ if (!rs.next()) {
+ return false;
}
- if (rs != null) {
- try {
- rs.close();
- } catch (SQLException ex) {
- // ignore
- }
+
+ final boolean auroraUtils = rs.getBoolean("aurora_stat_utils");
+ LOGGER.finest(Messages.get("AuroraPgDialect.auroraUtils", new Object[] {auroraUtils}));
+ if (auroraUtils) {
+ hasExtensions = true;
}
+ } catch (SQLException ex) {
+ return false;
}
+
if (!hasExtensions) {
return false;
}
- try {
- stmt = connection.createStatement();
- rs = stmt.executeQuery(topologySql);
- if (rs.next()) {
- LOGGER.finest(() -> "hasTopology: true");
- hasTopology = true;
- }
- } catch (final SQLException ex) {
- // ignore
- } finally {
- if (stmt != null) {
- try {
- stmt.close();
- } catch (SQLException ex) {
- // ignore
- }
- }
- if (rs != null) {
- try {
- rs.close();
- } catch (SQLException ex) {
- // ignore
- }
- }
- }
- return hasExtensions && hasTopology;
+
+ return dialectUtils.checkExistenceQueries(connection, TOPOLOGY_EXISTS_QUERY);
+ }
+
+ @Override
+ public List getDialectUpdateCandidates() {
+ return Arrays.asList(DialectCodes.GLOBAL_AURORA_PG,
+ DialectCodes.RDS_MULTI_AZ_PG_CLUSTER,
+ DialectCodes.RDS_PG);
}
@Override
- public HostListProviderSupplier getHostListProvider() {
+ public HostListProviderSupplier getHostListProviderSupplier() {
return (properties, initialUrl, servicesContainer) -> {
final PluginService pluginService = servicesContainer.getPluginService();
+ final TopologyUtils topologyUtils = new AuroraTopologyUtils(this, pluginService.getHostSpecBuilder());
if (pluginService.isPluginInUse(FailoverConnectionPlugin.class)) {
- return new MonitoringRdsHostListProvider(
- properties,
- initialUrl,
- servicesContainer,
- topologyQuery,
- nodeIdQuery,
- isReaderQuery,
- isWriterQuery);
+ return new MonitoringRdsHostListProvider(topologyUtils, properties, initialUrl, servicesContainer);
}
- return new AuroraHostListProvider(
- properties,
- initialUrl,
- servicesContainer,
- topologyQuery,
- nodeIdQuery,
- isReaderQuery);
+ return new RdsHostListProvider(topologyUtils, properties, initialUrl, servicesContainer);
};
}
+ @Override
+ public String getTopologyQuery() {
+ return TOPOLOGY_QUERY;
+ }
+
+ @Override
+ public String getInstanceIdQuery() {
+ return INSTANCE_ID_QUERY;
+ }
+
+ @Override
+ public String getWriterIdQuery() {
+ return WRITER_ID_QUERY;
+ }
+
+ @Override
+ public String getIsReaderQuery() {
+ return IS_READER_QUERY;
+ }
+
@Override
public String getLimitlessRouterEndpointQuery() {
return LIMITLESS_ROUTER_ENDPOINT_QUERY;
}
@Override
- public String getBlueGreenStatusQuery() {
- return BG_STATUS_QUERY;
+ public boolean isBlueGreenStatusAvailable(final Connection connection) {
+ return dialectUtils.checkExistenceQueries(connection, BG_TOPOLOGY_EXISTS_QUERY);
}
@Override
- public boolean isBlueGreenStatusAvailable(final Connection connection) {
- try {
- try (Statement statement = connection.createStatement();
- ResultSet rs = statement.executeQuery(TOPOLOGY_TABLE_EXIST_QUERY)) {
- return rs.next();
- }
- } catch (SQLException ex) {
- return false;
- }
+ public String getBlueGreenStatusQuery() {
+ return BG_STATUS_QUERY;
}
}
diff --git a/wrapper/src/main/java/software/amazon/jdbc/dialect/BlueGreenDialect.java b/wrapper/src/main/java/software/amazon/jdbc/dialect/BlueGreenDialect.java
index ce1b678d3..a5e34f150 100644
--- a/wrapper/src/main/java/software/amazon/jdbc/dialect/BlueGreenDialect.java
+++ b/wrapper/src/main/java/software/amazon/jdbc/dialect/BlueGreenDialect.java
@@ -19,7 +19,7 @@
import java.sql.Connection;
public interface BlueGreenDialect {
- String getBlueGreenStatusQuery();
-
boolean isBlueGreenStatusAvailable(final Connection connection);
+
+ String getBlueGreenStatusQuery();
}
diff --git a/wrapper/src/main/java/software/amazon/jdbc/dialect/Dialect.java b/wrapper/src/main/java/software/amazon/jdbc/dialect/Dialect.java
index 367db7d25..5f09aae0b 100644
--- a/wrapper/src/main/java/software/amazon/jdbc/dialect/Dialect.java
+++ b/wrapper/src/main/java/software/amazon/jdbc/dialect/Dialect.java
@@ -26,22 +26,23 @@
import software.amazon.jdbc.plugin.failover.FailoverRestriction;
public interface Dialect {
- int getDefaultPort();
- ExceptionHandler getExceptionHandler();
+ boolean isDialect(Connection connection);
- String getHostAliasQuery();
+ int getDefaultPort();
- String getServerVersionQuery();
+ List* dialect code */ String> getDialectUpdateCandidates();
- boolean isDialect(Connection connection);
+ ExceptionHandler getExceptionHandler();
- List* dialect code */ String> getDialectUpdateCandidates();
+ HostListProviderSupplier getHostListProviderSupplier();
- HostListProviderSupplier getHostListProvider();
+ String getHostAliasQuery();
void prepareConnectProperties(
final @NonNull Properties connectProperties, final @NonNull String protocol, final @NonNull HostSpec hostSpec);
EnumSet getFailoverRestrictions();
+
+ String getServerVersionQuery();
}
diff --git a/wrapper/src/main/java/software/amazon/jdbc/dialect/DialectManager.java b/wrapper/src/main/java/software/amazon/jdbc/dialect/DialectManager.java
index 273edf82a..ed7f4e71e 100644
--- a/wrapper/src/main/java/software/amazon/jdbc/dialect/DialectManager.java
+++ b/wrapper/src/main/java/software/amazon/jdbc/dialect/DialectManager.java
@@ -57,9 +57,9 @@ public class DialectManager implements DialectProvider {
put(DialectCodes.PG, new PgDialect());
put(DialectCodes.MARIADB, new MariaDbDialect());
put(DialectCodes.RDS_MYSQL, new RdsMysqlDialect());
- put(DialectCodes.RDS_MULTI_AZ_MYSQL_CLUSTER, new RdsMultiAzDbClusterMysqlDialect());
+ put(DialectCodes.RDS_MULTI_AZ_MYSQL_CLUSTER, new MultiAzClusterMysqlDialect());
put(DialectCodes.RDS_PG, new RdsPgDialect());
- put(DialectCodes.RDS_MULTI_AZ_PG_CLUSTER, new RdsMultiAzDbClusterPgDialect());
+ put(DialectCodes.RDS_MULTI_AZ_PG_CLUSTER, new MultiAzClusterPgDialect());
put(DialectCodes.GLOBAL_AURORA_MYSQL, new GlobalAuroraMysqlDialect());
put(DialectCodes.AURORA_MYSQL, new AuroraMysqlDialect());
put(DialectCodes.GLOBAL_AURORA_PG, new GlobalAuroraPgDialect());
@@ -75,7 +75,7 @@ public class DialectManager implements DialectProvider {
*/
protected static final long ENDPOINT_CACHE_EXPIRATION = TimeUnit.HOURS.toNanos(24);
- // Map of host name, or url, by dialect code.
+ // Keys are host names or URLs, values are dialect codes.
protected static final CacheMap knownEndpointDialects = new CacheMap<>();
private final RdsUtils rdsHelper = new RdsUtils();
@@ -129,8 +129,7 @@ public Dialect getDialect(
this.logCurrentDialect();
return userDialect;
} else {
- throw new SQLException(
- Messages.get("DialectManager.unknownDialectCode", new Object[] {dialectCode}));
+ throw new SQLException(Messages.get("DialectManager.unknownDialectCode", new Object[] {dialectCode}));
}
}
@@ -140,7 +139,7 @@ public Dialect getDialect(
String host = url;
final List hosts = this.connectionUrlParser.getHostsFromConnectionUrl(
- url, true, pluginService::getHostSpecBuilder);
+ url, true, pluginService::getHostSpecBuilder);
if (!Utils.isNullOrEmpty(hosts)) {
host = hosts.get(0).getHost();
}
@@ -238,9 +237,10 @@ public Dialect getDialect(
for (String dialectCandidateCode : dialectCandidates) {
Dialect dialectCandidate = knownDialectsByCode.get(dialectCandidateCode);
if (dialectCandidate == null) {
- throw new SQLException(
- Messages.get("DialectManager.unknownDialectCode", new Object[] {dialectCandidateCode}));
+ throw new SQLException(Messages.get(
+ "DialectManager.unknownDialectCode", new Object[] {dialectCandidateCode}));
}
+
boolean isDialect = dialectCandidate.isDialect(connection);
if (isDialect) {
this.canUpdate = false;
@@ -270,9 +270,8 @@ public Dialect getDialect(
}
private void logCurrentDialect() {
- LOGGER.finest(() -> String.format("Current dialect: %s, %s, canUpdate: %b",
- this.dialectCode,
- this.dialect == null ? "" : this.dialect,
- this.canUpdate));
+ LOGGER.finest(Messages.get(
+ "DialectManager.currentDialect",
+ new Object[] {this.dialectCode, this.dialect == null ? "" : this.dialect, this.canUpdate}));
}
}
diff --git a/wrapper/src/main/java/software/amazon/jdbc/dialect/DialectUtils.java b/wrapper/src/main/java/software/amazon/jdbc/dialect/DialectUtils.java
new file mode 100644
index 000000000..a09480cd7
--- /dev/null
+++ b/wrapper/src/main/java/software/amazon/jdbc/dialect/DialectUtils.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package software.amazon.jdbc.dialect;
+
+import java.sql.Connection;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+
+public class DialectUtils {
+ /**
+ * Given a series of existence queries, returns true if they all execute successfully and contain at least one record.
+ * Otherwise, returns false.
+ *
+ * @param conn the connection to use for executing the queries.
+ * @param existenceQueries the queries to check for existing records.
+ * @return true if all queries execute successfully and return at least one record, false otherwise.
+ */
+ public boolean checkExistenceQueries(Connection conn, String... existenceQueries) {
+ for (String existenceQuery : existenceQueries) {
+ try (Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery(existenceQuery)) {
+ if (!rs.next()) {
+ return false;
+ }
+ } catch (SQLException e) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+}
diff --git a/wrapper/src/main/java/software/amazon/jdbc/dialect/GlobalAuroraMysqlDialect.java b/wrapper/src/main/java/software/amazon/jdbc/dialect/GlobalAuroraMysqlDialect.java
index 3e4db74e6..334757af9 100644
--- a/wrapper/src/main/java/software/amazon/jdbc/dialect/GlobalAuroraMysqlDialect.java
+++ b/wrapper/src/main/java/software/amazon/jdbc/dialect/GlobalAuroraMysqlDialect.java
@@ -23,79 +23,48 @@
import java.util.Collections;
import java.util.List;
import software.amazon.jdbc.PluginService;
-import software.amazon.jdbc.hostlistprovider.AuroraGlobalDbHostListProvider;
-import software.amazon.jdbc.hostlistprovider.monitoring.AuroraGlobalDbMonitoringHostListProvider;
+import software.amazon.jdbc.hostlistprovider.GlobalAuroraHostListProvider;
+import software.amazon.jdbc.hostlistprovider.GlobalAuroraTopologyUtils;
+import software.amazon.jdbc.hostlistprovider.monitoring.MonitoringGlobalAuroraHostListProvider;
import software.amazon.jdbc.plugin.failover2.FailoverConnectionPlugin;
-public class GlobalAuroraMysqlDialect extends AuroraMysqlDialect {
+public class GlobalAuroraMysqlDialect extends AuroraMysqlDialect implements GlobalAuroraTopologyDialect {
- protected final String globalDbStatusTableExistQuery =
+ protected static final String GLOBAL_STATUS_TABLE_EXISTS_QUERY =
"SELECT 1 AS tmp FROM information_schema.tables WHERE"
+ " upper(table_schema) = 'INFORMATION_SCHEMA' AND upper(table_name) = 'AURORA_GLOBAL_DB_STATUS'";
-
- protected final String globalDbStatusQuery =
- "SELECT count(1) FROM information_schema.aurora_global_db_status";
-
- protected final String globalDbInstanceStatusTableExistQuery =
+ protected static final String GLOBAL_INSTANCE_STATUS_EXISTS_QUERY =
"SELECT 1 AS tmp FROM information_schema.tables WHERE"
+ " upper(table_schema) = 'INFORMATION_SCHEMA' AND upper(table_name) = 'AURORA_GLOBAL_DB_INSTANCE_STATUS'";
- protected final String globalTopologyQuery =
+ protected static final String GLOBAL_TOPOLOGY_QUERY =
"SELECT SERVER_ID, CASE WHEN SESSION_ID = 'MASTER_SESSION_ID' THEN TRUE ELSE FALSE END, "
+ "VISIBILITY_LAG_IN_MSEC, AWS_REGION "
+ "FROM information_schema.aurora_global_db_instance_status ";
- protected final String regionByNodeIdQuery =
+ protected static final String REGION_COUNT_QUERY = "SELECT count(1) FROM information_schema.aurora_global_db_status";
+ protected static final String REGION_BY_INSTANCE_ID_QUERY =
"SELECT AWS_REGION FROM information_schema.aurora_global_db_instance_status WHERE SERVER_ID = ?";
+
@Override
public boolean isDialect(final Connection connection) {
- Statement stmt = null;
- ResultSet rs = null;
- try {
- stmt = connection.createStatement();
- rs = stmt.executeQuery(this.globalDbStatusTableExistQuery);
-
- if (rs.next()) {
- rs.close();
- stmt.close();
-
- stmt = connection.createStatement();
- rs = stmt.executeQuery(this.globalDbInstanceStatusTableExistQuery);
-
- if (rs.next()) {
- rs.close();
- stmt.close();
-
- stmt = connection.createStatement();
- rs = stmt.executeQuery(this.globalDbStatusQuery);
+ if (!dialectUtils.checkExistenceQueries(
+ connection, GLOBAL_STATUS_TABLE_EXISTS_QUERY, GLOBAL_INSTANCE_STATUS_EXISTS_QUERY)) {
+ return false;
+ }
- if (rs.next()) {
- int awsRegionCount = rs.getInt(1);
- return awsRegionCount > 1;
- }
- }
+ try (Statement stmt = connection.createStatement();
+ ResultSet rs = stmt.executeQuery(REGION_COUNT_QUERY)) {
+ if (!rs.next()) {
+ return false;
}
- return false;
+
+ int awsRegionCount = rs.getInt(1);
+ return awsRegionCount > 1;
} catch (final SQLException ex) {
- // ignore
- } finally {
- if (rs != null) {
- try {
- rs.close();
- } catch (SQLException ex) {
- // ignore
- }
- }
- if (stmt != null) {
- try {
- stmt.close();
- } catch (SQLException ex) {
- // ignore
- }
- }
+ return false;
}
- return false;
}
@Override
@@ -104,27 +73,25 @@ public boolean isDialect(final Connection connection) {
}
@Override
- public HostListProviderSupplier getHostListProvider() {
+ public HostListProviderSupplier getHostListProviderSupplier() {
return (properties, initialUrl, servicesContainer) -> {
final PluginService pluginService = servicesContainer.getPluginService();
+ final GlobalAuroraTopologyUtils topologyUtils =
+ new GlobalAuroraTopologyUtils(this, pluginService.getHostSpecBuilder());
if (pluginService.isPluginInUse(FailoverConnectionPlugin.class)) {
- return new AuroraGlobalDbMonitoringHostListProvider(
- properties,
- initialUrl,
- servicesContainer,
- this.globalTopologyQuery,
- this.nodeIdQuery,
- this.isReaderQuery,
- this.isWriterQuery,
- this.regionByNodeIdQuery);
+ return new MonitoringGlobalAuroraHostListProvider(topologyUtils, properties, initialUrl, servicesContainer);
}
- return new AuroraGlobalDbHostListProvider(
- properties,
- initialUrl,
- servicesContainer,
- this.globalTopologyQuery,
- this.nodeIdQuery,
- this.isReaderQuery);
+ return new GlobalAuroraHostListProvider(topologyUtils, properties, initialUrl, servicesContainer);
};
}
+
+ @Override
+ public String getTopologyQuery() {
+ return GLOBAL_TOPOLOGY_QUERY;
+ }
+
+ @Override
+ public String getRegionByInstanceIdQuery() {
+ return REGION_BY_INSTANCE_ID_QUERY;
+ }
}
diff --git a/wrapper/src/main/java/software/amazon/jdbc/dialect/GlobalAuroraPgDialect.java b/wrapper/src/main/java/software/amazon/jdbc/dialect/GlobalAuroraPgDialect.java
index 289ced4ae..7c060c800 100644
--- a/wrapper/src/main/java/software/amazon/jdbc/dialect/GlobalAuroraPgDialect.java
+++ b/wrapper/src/main/java/software/amazon/jdbc/dialect/GlobalAuroraPgDialect.java
@@ -24,91 +24,62 @@
import java.util.List;
import java.util.logging.Logger;
import software.amazon.jdbc.PluginService;
-import software.amazon.jdbc.hostlistprovider.AuroraGlobalDbHostListProvider;
-import software.amazon.jdbc.hostlistprovider.monitoring.AuroraGlobalDbMonitoringHostListProvider;
+import software.amazon.jdbc.hostlistprovider.GlobalAuroraHostListProvider;
+import software.amazon.jdbc.hostlistprovider.GlobalAuroraTopologyUtils;
+import software.amazon.jdbc.hostlistprovider.monitoring.MonitoringGlobalAuroraHostListProvider;
import software.amazon.jdbc.plugin.failover2.FailoverConnectionPlugin;
+import software.amazon.jdbc.util.Messages;
-public class GlobalAuroraPgDialect extends AuroraPgDialect {
+public class GlobalAuroraPgDialect extends AuroraPgDialect implements GlobalAuroraTopologyDialect {
- private static final Logger LOGGER = Logger.getLogger(GlobalAuroraPgDialect.class.getName());
-
- protected final String globalDbStatusFuncExistQuery =
- "select 'aurora_global_db_status'::regproc";
-
- protected final String globalDbInstanceStatusFuncExistQuery =
+ protected static final String GLOBAL_STATUS_FUNC_EXISTS_QUERY = "select 'aurora_global_db_status'::regproc";
+ protected static final String GLOBAL_INSTANCE_STATUS_FUNC_EXISTS_QUERY =
"select 'aurora_global_db_instance_status'::regproc";
- protected final String globalTopologyQuery =
+ protected static final String GLOBAL_TOPOLOGY_QUERY =
"SELECT SERVER_ID, CASE WHEN SESSION_ID = 'MASTER_SESSION_ID' THEN TRUE ELSE FALSE END, "
+ "VISIBILITY_LAG_IN_MSEC, AWS_REGION "
+ "FROM aurora_global_db_instance_status()";
- protected final String globalDbStatusQuery =
- "SELECT count(1) FROM aurora_global_db_status()";
-
- protected final String regionByNodeIdQuery =
+ protected static final String REGION_COUNT_QUERY = "SELECT count(1) FROM aurora_global_db_status()";
+ protected static final String REGION_BY_INSTANCE_ID_QUERY =
"SELECT AWS_REGION FROM aurora_global_db_instance_status() WHERE SERVER_ID = ?";
+ private static final Logger LOGGER = Logger.getLogger(GlobalAuroraPgDialect.class.getName());
+
@Override
public boolean isDialect(final Connection connection) {
- Statement stmt = null;
- ResultSet rs = null;
try {
- stmt = connection.createStatement();
- rs = stmt.executeQuery(this.extensionsSql);
- if (rs.next()) {
+ try (Statement stmt = connection.createStatement();
+ ResultSet rs = stmt.executeQuery(AURORA_UTILS_EXIST_QUERY)) {
+ if (!rs.next()) {
+ return false;
+ }
+
final boolean auroraUtils = rs.getBoolean("aurora_stat_utils");
- LOGGER.finest(() -> String.format("auroraUtils: %b", auroraUtils));
+ LOGGER.finest(Messages.get("AuroraPgDialect.auroraUtils", new Object[] {auroraUtils}));
if (!auroraUtils) {
return false;
}
}
- rs.close();
- stmt.close();
-
- stmt = connection.createStatement();
- rs = stmt.executeQuery(this.globalDbStatusFuncExistQuery);
-
- if (rs.next()) {
- rs.close();
- stmt.close();
- stmt = connection.createStatement();
- rs = stmt.executeQuery(this.globalDbInstanceStatusFuncExistQuery);
-
- if (rs.next()) {
- rs.close();
- stmt.close();
-
- stmt = connection.createStatement();
- rs = stmt.executeQuery(this.globalDbStatusQuery);
+ if (!dialectUtils.checkExistenceQueries(
+ connection, GLOBAL_STATUS_FUNC_EXISTS_QUERY, GLOBAL_INSTANCE_STATUS_FUNC_EXISTS_QUERY)) {
+ return false;
+ }
- if (rs.next()) {
- int awsRegionCount = rs.getInt(1);
- return awsRegionCount > 1;
- }
+ try (Statement stmt = connection.createStatement();
+ ResultSet rs = stmt.executeQuery(REGION_COUNT_QUERY)) {
+ if (!rs.next()) {
+ return false;
}
+
+ int awsRegionCount = rs.getInt(1);
+ return awsRegionCount > 1;
}
- return false;
} catch (final SQLException ex) {
- // ignore
- } finally {
- if (rs != null) {
- try {
- rs.close();
- } catch (SQLException ex) {
- // ignore
- }
- }
- if (stmt != null) {
- try {
- stmt.close();
- } catch (SQLException ex) {
- // ignore
- }
- }
+ return false;
}
- return false;
}
@Override
@@ -117,27 +88,25 @@ public boolean isDialect(final Connection connection) {
}
@Override
- public HostListProviderSupplier getHostListProvider() {
+ public HostListProviderSupplier getHostListProviderSupplier() {
return (properties, initialUrl, servicesContainer) -> {
final PluginService pluginService = servicesContainer.getPluginService();
+ final GlobalAuroraTopologyUtils topologyUtils =
+ new GlobalAuroraTopologyUtils(this, pluginService.getHostSpecBuilder());
if (pluginService.isPluginInUse(FailoverConnectionPlugin.class)) {
- return new AuroraGlobalDbMonitoringHostListProvider(
- properties,
- initialUrl,
- servicesContainer,
- this.globalTopologyQuery,
- this.nodeIdQuery,
- this.isReaderQuery,
- this.isWriterQuery,
- this.regionByNodeIdQuery);
+ return new MonitoringGlobalAuroraHostListProvider(topologyUtils, properties, initialUrl, servicesContainer);
}
- return new AuroraGlobalDbHostListProvider(
- properties,
- initialUrl,
- servicesContainer,
- this.globalTopologyQuery,
- this.nodeIdQuery,
- this.isReaderQuery);
+ return new GlobalAuroraHostListProvider(topologyUtils, properties, initialUrl, servicesContainer);
};
}
+
+ @Override
+ public String getTopologyQuery() {
+ return GLOBAL_TOPOLOGY_QUERY;
+ }
+
+ @Override
+ public String getRegionByInstanceIdQuery() {
+ return REGION_BY_INSTANCE_ID_QUERY;
+ }
}
diff --git a/wrapper/src/main/java/software/amazon/jdbc/dialect/GlobalAuroraTopologyDialect.java b/wrapper/src/main/java/software/amazon/jdbc/dialect/GlobalAuroraTopologyDialect.java
new file mode 100644
index 000000000..11db48dff
--- /dev/null
+++ b/wrapper/src/main/java/software/amazon/jdbc/dialect/GlobalAuroraTopologyDialect.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package software.amazon.jdbc.dialect;
+
+public interface GlobalAuroraTopologyDialect extends TopologyDialect {
+ String getRegionByInstanceIdQuery();
+}
diff --git a/wrapper/src/main/java/software/amazon/jdbc/dialect/HostListProviderSupplier.java b/wrapper/src/main/java/software/amazon/jdbc/dialect/HostListProviderSupplier.java
index 0dfe44dc5..bee378f9f 100644
--- a/wrapper/src/main/java/software/amazon/jdbc/dialect/HostListProviderSupplier.java
+++ b/wrapper/src/main/java/software/amazon/jdbc/dialect/HostListProviderSupplier.java
@@ -18,7 +18,7 @@
import java.util.Properties;
import org.checkerframework.checker.nullness.qual.NonNull;
-import software.amazon.jdbc.HostListProvider;
+import software.amazon.jdbc.hostlistprovider.HostListProvider;
import software.amazon.jdbc.util.FullServicesContainer;
@FunctionalInterface
diff --git a/wrapper/src/main/java/software/amazon/jdbc/dialect/MariaDbDialect.java b/wrapper/src/main/java/software/amazon/jdbc/dialect/MariaDbDialect.java
index 3b368a8a1..58453f6fa 100644
--- a/wrapper/src/main/java/software/amazon/jdbc/dialect/MariaDbDialect.java
+++ b/wrapper/src/main/java/software/amazon/jdbc/dialect/MariaDbDialect.java
@@ -32,45 +32,23 @@
import software.amazon.jdbc.plugin.failover.FailoverRestriction;
public class MariaDbDialect implements Dialect {
+
+ protected static final String VERSION_QUERY = "SELECT VERSION()";
+ protected static final String HOST_ALIAS_QUERY = "SELECT CONCAT(@@hostname, ':', @@port)";
+
+ private static MariaDBExceptionHandler mariaDBExceptionHandler;
+ private static final EnumSet NO_FAILOVER_RESTRICTIONS =
+ EnumSet.noneOf(FailoverRestriction.class);
private static final List dialectUpdateCandidates = Arrays.asList(
DialectCodes.AURORA_MYSQL,
DialectCodes.RDS_MULTI_AZ_MYSQL_CLUSTER,
DialectCodes.RDS_MYSQL,
DialectCodes.MYSQL);
- private static MariaDBExceptionHandler mariaDBExceptionHandler;
-
- private static final EnumSet NO_RESTRICTIONS = EnumSet.noneOf(FailoverRestriction.class);
-
- @Override
- public int getDefaultPort() {
- return 3306;
- }
-
- @Override
- public ExceptionHandler getExceptionHandler() {
- if (mariaDBExceptionHandler == null) {
- mariaDBExceptionHandler = new MariaDBExceptionHandler();
- }
- return mariaDBExceptionHandler;
- }
-
- @Override
- public String getHostAliasQuery() {
- return "SELECT CONCAT(@@hostname, ':', @@port)";
- }
-
- @Override
- public String getServerVersionQuery() {
- return "SELECT VERSION()";
- }
@Override
public boolean isDialect(final Connection connection) {
- Statement stmt = null;
- ResultSet rs = null;
- try {
- stmt = connection.createStatement();
- rs = stmt.executeQuery(this.getServerVersionQuery());
+ try (Statement stmt = connection.createStatement();
+ ResultSet rs = stmt.executeQuery(VERSION_QUERY)) {
while (rs.next()) {
final String columnValue = rs.getString(1);
if (columnValue != null && columnValue.toLowerCase().contains("mariadb")) {
@@ -78,32 +56,31 @@ public boolean isDialect(final Connection connection) {
}
}
} catch (final SQLException ex) {
- // ignore
- } finally {
- if (stmt != null) {
- try {
- stmt.close();
- } catch (SQLException ex) {
- // ignore
- }
- }
- if (rs != null) {
- try {
- rs.close();
- } catch (SQLException ex) {
- // ignore
- }
- }
+ return false;
}
+
return false;
}
+ @Override
+ public int getDefaultPort() {
+ return 3306;
+ }
+
@Override
public List getDialectUpdateCandidates() {
return dialectUpdateCandidates;
}
- public HostListProviderSupplier getHostListProvider() {
+ @Override
+ public ExceptionHandler getExceptionHandler() {
+ if (mariaDBExceptionHandler == null) {
+ mariaDBExceptionHandler = new MariaDBExceptionHandler();
+ }
+ return mariaDBExceptionHandler;
+ }
+
+ public HostListProviderSupplier getHostListProviderSupplier() {
return (properties, initialUrl, servicesContainer) ->
new ConnectionStringHostListProvider(properties, initialUrl, servicesContainer.getHostListProviderService());
}
@@ -116,6 +93,16 @@ public void prepareConnectProperties(
@Override
public EnumSet getFailoverRestrictions() {
- return NO_RESTRICTIONS;
+ return NO_FAILOVER_RESTRICTIONS;
+ }
+
+ @Override
+ public String getServerVersionQuery() {
+ return VERSION_QUERY;
+ }
+
+ @Override
+ public String getHostAliasQuery() {
+ return HOST_ALIAS_QUERY;
}
}
diff --git a/wrapper/src/test/java/integration/container/aurora/TestAuroraHostListProvider.java b/wrapper/src/main/java/software/amazon/jdbc/dialect/MultiAzClusterDialect.java
similarity index 57%
rename from wrapper/src/test/java/integration/container/aurora/TestAuroraHostListProvider.java
rename to wrapper/src/main/java/software/amazon/jdbc/dialect/MultiAzClusterDialect.java
index 5303763f7..4dc0a584d 100644
--- a/wrapper/src/test/java/integration/container/aurora/TestAuroraHostListProvider.java
+++ b/wrapper/src/main/java/software/amazon/jdbc/dialect/MultiAzClusterDialect.java
@@ -14,16 +14,10 @@
* limitations under the License.
*/
-package integration.container.aurora;
+package software.amazon.jdbc.dialect;
-import java.util.Properties;
-import software.amazon.jdbc.hostlistprovider.AuroraHostListProvider;
-import software.amazon.jdbc.util.FullServicesContainer;
+public interface MultiAzClusterDialect extends TopologyDialect {
+ String getWriterIdQuery();
-public class TestAuroraHostListProvider extends AuroraHostListProvider {
-
- public TestAuroraHostListProvider(
- FullServicesContainer servicesContainer, Properties properties, String originalUrl) {
- super(properties, originalUrl, servicesContainer, "", "", "");
- }
+ String getWriterIdColumnName();
}
diff --git a/wrapper/src/main/java/software/amazon/jdbc/dialect/RdsMultiAzDbClusterMysqlDialect.java b/wrapper/src/main/java/software/amazon/jdbc/dialect/MultiAzClusterMysqlDialect.java
similarity index 52%
rename from wrapper/src/main/java/software/amazon/jdbc/dialect/RdsMultiAzDbClusterMysqlDialect.java
rename to wrapper/src/main/java/software/amazon/jdbc/dialect/MultiAzClusterMysqlDialect.java
index 9c920c8e6..9fe7d3b03 100644
--- a/wrapper/src/main/java/software/amazon/jdbc/dialect/RdsMultiAzDbClusterMysqlDialect.java
+++ b/wrapper/src/main/java/software/amazon/jdbc/dialect/MultiAzClusterMysqlDialect.java
@@ -26,69 +26,56 @@
import org.checkerframework.checker.nullness.qual.NonNull;
import software.amazon.jdbc.HostSpec;
import software.amazon.jdbc.PluginService;
-import software.amazon.jdbc.hostlistprovider.RdsMultiAzDbClusterListProvider;
-import software.amazon.jdbc.hostlistprovider.monitoring.MonitoringRdsMultiAzHostListProvider;
+import software.amazon.jdbc.hostlistprovider.MultiAzTopologyUtils;
+import software.amazon.jdbc.hostlistprovider.RdsHostListProvider;
+import software.amazon.jdbc.hostlistprovider.TopologyUtils;
+import software.amazon.jdbc.hostlistprovider.monitoring.MonitoringRdsHostListProvider;
import software.amazon.jdbc.plugin.failover.FailoverRestriction;
import software.amazon.jdbc.plugin.failover2.FailoverConnectionPlugin;
import software.amazon.jdbc.util.DriverInfo;
import software.amazon.jdbc.util.RdsUtils;
import software.amazon.jdbc.util.StringUtils;
-public class RdsMultiAzDbClusterMysqlDialect extends MysqlDialect {
+public class MultiAzClusterMysqlDialect extends MysqlDialect implements MultiAzClusterDialect {
- private static final String TOPOLOGY_QUERY = "SELECT id, endpoint, port FROM mysql.rds_topology";
-
- private static final String TOPOLOGY_TABLE_EXIST_QUERY =
+ protected static final String REPORT_HOST_EXISTS_QUERY = "SHOW VARIABLES LIKE 'report_host'";
+ protected static final String TOPOLOGY_TABLE_EXISTS_QUERY =
"SELECT 1 AS tmp FROM information_schema.tables WHERE"
- + " table_schema = 'mysql' AND table_name = 'rds_topology'";
-
- // For reader nodes, the query returns a writer node ID. For a writer node, the query returns no data.
- private static final String FETCH_WRITER_NODE_QUERY = "SHOW REPLICA STATUS";
-
- private static final String FETCH_WRITER_NODE_QUERY_COLUMN_NAME = "Source_Server_Id";
+ + " table_schema = 'mysql' AND table_name = 'rds_topology'";
+ protected static final String TOPOLOGY_QUERY = "SELECT id, endpoint, port FROM mysql.rds_topology";
- // The query return nodeId and nodeName.
+ // This query returns both instanceId and instanceName.
// For example: "1845128080", "test-multiaz-instance-1"
- private static final String NODE_ID_QUERY = "SELECT id, SUBSTRING_INDEX(endpoint, '.', 1)"
+ protected static final String INSTANCE_ID_QUERY = "SELECT id, SUBSTRING_INDEX(endpoint, '.', 1)"
+ " FROM mysql.rds_topology"
+ " WHERE id = @@server_id";
- private static final String IS_READER_QUERY = "SELECT @@read_only";
+ // For reader instances, this query returns a writer instance ID. For a writer instance, this query returns no data.
+ protected static final String WRITER_ID_QUERY = "SHOW REPLICA STATUS";
+ protected static final String WRITER_ID_QUERY_COLUMN_NAME = "Source_Server_Id";
+ protected static final String IS_READER_QUERY = "SELECT @@read_only";
- private static final EnumSet RDS_MULTI_AZ_RESTRICTIONS =
+ private static final EnumSet FAILOVER_RESTRICTIONS =
EnumSet.of(FailoverRestriction.DISABLE_TASK_A, FailoverRestriction.ENABLE_WRITER_IN_TASK_B);
protected final RdsUtils rdsUtils = new RdsUtils();
@Override
public boolean isDialect(final Connection connection) {
- try {
- try (Statement stmt = connection.createStatement();
- ResultSet rs = stmt.executeQuery(TOPOLOGY_TABLE_EXIST_QUERY)) {
- if (!rs.next()) {
- return false;
- }
- }
-
- try (Statement stmt = connection.createStatement();
- ResultSet rs = stmt.executeQuery(TOPOLOGY_QUERY)) {
- if (!rs.next()) {
- return false;
- }
- }
+ if (!dialectUtils.checkExistenceQueries(connection, TOPOLOGY_TABLE_EXISTS_QUERY, TOPOLOGY_QUERY)) {
+ return false;
+ }
- try (Statement stmt = connection.createStatement();
- ResultSet rs = stmt.executeQuery("SHOW VARIABLES LIKE 'report_host'")) {
- if (!rs.next()) {
- return false;
- }
- final String reportHost = rs.getString(2); // get variable value; expected value is IP address
- return !StringUtils.isNullOrEmpty(reportHost);
+ try (Statement stmt = connection.createStatement();
+ ResultSet rs = stmt.executeQuery(REPORT_HOST_EXISTS_QUERY)) {
+ if (!rs.next()) {
+ return false;
}
+ final String reportHost = rs.getString(2); // Expected value is an IP address
+ return !StringUtils.isNullOrEmpty(reportHost);
} catch (final SQLException ex) {
- // ignore
+ return false;
}
- return false;
}
@Override
@@ -97,31 +84,14 @@ public boolean isDialect(final Connection connection) {
}
@Override
- public HostListProviderSupplier getHostListProvider() {
+ public HostListProviderSupplier getHostListProviderSupplier() {
return (properties, initialUrl, servicesContainer) -> {
final PluginService pluginService = servicesContainer.getPluginService();
+ final TopologyUtils topologyUtils = new MultiAzTopologyUtils(this, pluginService.getHostSpecBuilder());
if (pluginService.isPluginInUse(FailoverConnectionPlugin.class)) {
- return new MonitoringRdsMultiAzHostListProvider(
- properties,
- initialUrl,
- servicesContainer,
- TOPOLOGY_QUERY,
- NODE_ID_QUERY,
- IS_READER_QUERY,
- FETCH_WRITER_NODE_QUERY,
- FETCH_WRITER_NODE_QUERY_COLUMN_NAME);
-
- } else {
- return new RdsMultiAzDbClusterListProvider(
- properties,
- initialUrl,
- servicesContainer,
- TOPOLOGY_QUERY,
- NODE_ID_QUERY,
- IS_READER_QUERY,
- FETCH_WRITER_NODE_QUERY,
- FETCH_WRITER_NODE_QUERY_COLUMN_NAME);
+ return new MonitoringRdsHostListProvider(topologyUtils, properties, initialUrl, servicesContainer);
}
+ return new RdsHostListProvider(topologyUtils, properties, initialUrl, servicesContainer);
};
}
@@ -138,6 +108,31 @@ public void prepareConnectProperties(
@Override
public EnumSet getFailoverRestrictions() {
- return RDS_MULTI_AZ_RESTRICTIONS;
+ return FAILOVER_RESTRICTIONS;
+ }
+
+ @Override
+ public String getTopologyQuery() {
+ return TOPOLOGY_QUERY;
+ }
+
+ @Override
+ public String getInstanceIdQuery() {
+ return INSTANCE_ID_QUERY;
+ }
+
+ @Override
+ public String getIsReaderQuery() {
+ return IS_READER_QUERY;
+ }
+
+ @Override
+ public String getWriterIdQuery() {
+ return WRITER_ID_QUERY;
+ }
+
+ @Override
+ public String getWriterIdColumnName() {
+ return WRITER_ID_QUERY_COLUMN_NAME;
}
}
diff --git a/wrapper/src/main/java/software/amazon/jdbc/dialect/RdsMultiAzDbClusterPgDialect.java b/wrapper/src/main/java/software/amazon/jdbc/dialect/MultiAzClusterPgDialect.java
similarity index 59%
rename from wrapper/src/main/java/software/amazon/jdbc/dialect/RdsMultiAzDbClusterPgDialect.java
rename to wrapper/src/main/java/software/amazon/jdbc/dialect/MultiAzClusterPgDialect.java
index 0f6f51301..ba3600710 100644
--- a/wrapper/src/main/java/software/amazon/jdbc/dialect/RdsMultiAzDbClusterPgDialect.java
+++ b/wrapper/src/main/java/software/amazon/jdbc/dialect/MultiAzClusterPgDialect.java
@@ -21,60 +21,48 @@
import java.sql.SQLException;
import java.sql.Statement;
import java.util.List;
-import java.util.logging.Logger;
import software.amazon.jdbc.PluginService;
import software.amazon.jdbc.exceptions.ExceptionHandler;
import software.amazon.jdbc.exceptions.MultiAzDbClusterPgExceptionHandler;
-import software.amazon.jdbc.hostlistprovider.RdsMultiAzDbClusterListProvider;
-import software.amazon.jdbc.hostlistprovider.monitoring.MonitoringRdsMultiAzHostListProvider;
+import software.amazon.jdbc.hostlistprovider.MultiAzTopologyUtils;
+import software.amazon.jdbc.hostlistprovider.RdsHostListProvider;
+import software.amazon.jdbc.hostlistprovider.TopologyUtils;
+import software.amazon.jdbc.hostlistprovider.monitoring.MonitoringRdsHostListProvider;
import software.amazon.jdbc.plugin.failover2.FailoverConnectionPlugin;
import software.amazon.jdbc.util.DriverInfo;
-public class RdsMultiAzDbClusterPgDialect extends PgDialect {
+public class MultiAzClusterPgDialect extends PgDialect implements MultiAzClusterDialect {
- private static final Logger LOGGER = Logger.getLogger(RdsMultiAzDbClusterPgDialect.class.getName());
-
- private static MultiAzDbClusterPgExceptionHandler exceptionHandler;
-
- private static final String TOPOLOGY_QUERY =
- "SELECT id, endpoint, port FROM rds_tools.show_topology('aws_jdbc_driver-" + DriverInfo.DRIVER_VERSION + "')";
-
- // For reader nodes, the query should return a writer node ID. For a writer node, the query should return no data.
- private static final String FETCH_WRITER_NODE_QUERY =
- "SELECT multi_az_db_cluster_source_dbi_resource_id FROM rds_tools.multi_az_db_cluster_source_dbi_resource_id()"
- + " WHERE multi_az_db_cluster_source_dbi_resource_id OPERATOR(pg_catalog.!=)"
- + " (SELECT dbi_resource_id FROM rds_tools.dbi_resource_id())";
-
- private static final String IS_RDS_CLUSTER_QUERY =
+ protected static final String IS_RDS_CLUSTER_QUERY =
"SELECT multi_az_db_cluster_source_dbi_resource_id FROM rds_tools.multi_az_db_cluster_source_dbi_resource_id()";
+ protected static final String TOPOLOGY_QUERY =
+ "SELECT id, endpoint, port FROM rds_tools.show_topology('aws_jdbc_driver-" + DriverInfo.DRIVER_VERSION + "')";
- private static final String FETCH_WRITER_NODE_QUERY_COLUMN_NAME = "multi_az_db_cluster_source_dbi_resource_id";
-
- // The query return nodeId and nodeName.
+ // This query returns both instanceId and instanceName.
// For example: "db-WQFQKBTL2LQUPIEFIFBGENS4ZQ", "test-multiaz-instance-1"
- private static final String NODE_ID_QUERY = "SELECT id, SUBSTRING(endpoint FROM 0 FOR POSITION('.' IN endpoint))"
+ protected static final String INSTANCE_ID_QUERY =
+ "SELECT id, SUBSTRING(endpoint FROM 0 FOR POSITION('.' IN endpoint))"
+ " FROM rds_tools.show_topology()"
+ " WHERE id OPERATOR(pg_catalog.=) rds_tools.dbi_resource_id()";
+ // For reader instances, this query should return a writer instance ID.
+ // For a writer instance, this query should return no data.
+ protected static final String WRITER_ID_QUERY =
+ "SELECT multi_az_db_cluster_source_dbi_resource_id FROM rds_tools.multi_az_db_cluster_source_dbi_resource_id()"
+ + " WHERE multi_az_db_cluster_source_dbi_resource_id OPERATOR(pg_catalog.!=)"
+ + " (SELECT dbi_resource_id FROM rds_tools.dbi_resource_id())";
+ protected static final String WRITER_ID_QUERY_COLUMN_NAME = "multi_az_db_cluster_source_dbi_resource_id";
+ protected static final String IS_READER_QUERY = "SELECT pg_catalog.pg_is_in_recovery()";
- private static final String IS_READER_QUERY = "SELECT pg_catalog.pg_is_in_recovery()";
-
- @Override
- public ExceptionHandler getExceptionHandler() {
- if (exceptionHandler == null) {
- exceptionHandler = new MultiAzDbClusterPgExceptionHandler();
- }
- return exceptionHandler;
- }
+ private static MultiAzDbClusterPgExceptionHandler exceptionHandler;
@Override
public boolean isDialect(final Connection connection) {
try (Statement stmt = connection.createStatement();
- ResultSet rs = stmt.executeQuery(IS_RDS_CLUSTER_QUERY)) {
+ ResultSet rs = stmt.executeQuery(IS_RDS_CLUSTER_QUERY)) {
return rs.next() && rs.getString(1) != null;
} catch (final SQLException ex) {
- // ignore
+ return false;
}
- return false;
}
@Override
@@ -83,32 +71,48 @@ public boolean isDialect(final Connection connection) {
}
@Override
- public HostListProviderSupplier getHostListProvider() {
+ public ExceptionHandler getExceptionHandler() {
+ if (exceptionHandler == null) {
+ exceptionHandler = new MultiAzDbClusterPgExceptionHandler();
+ }
+ return exceptionHandler;
+ }
+
+ @Override
+ public HostListProviderSupplier getHostListProviderSupplier() {
return (properties, initialUrl, servicesContainer) -> {
final PluginService pluginService = servicesContainer.getPluginService();
+ final TopologyUtils topologyUtils = new MultiAzTopologyUtils(this, pluginService.getHostSpecBuilder());
if (pluginService.isPluginInUse(FailoverConnectionPlugin.class)) {
- return new MonitoringRdsMultiAzHostListProvider(
- properties,
- initialUrl,
- servicesContainer,
- TOPOLOGY_QUERY,
- NODE_ID_QUERY,
- IS_READER_QUERY,
- FETCH_WRITER_NODE_QUERY,
- FETCH_WRITER_NODE_QUERY_COLUMN_NAME);
-
- } else {
-
- return new RdsMultiAzDbClusterListProvider(
- properties,
- initialUrl,
- servicesContainer,
- TOPOLOGY_QUERY,
- NODE_ID_QUERY,
- IS_READER_QUERY,
- FETCH_WRITER_NODE_QUERY,
- FETCH_WRITER_NODE_QUERY_COLUMN_NAME);
+ return new MonitoringRdsHostListProvider(topologyUtils, properties, initialUrl, servicesContainer);
}
+
+ return new RdsHostListProvider(topologyUtils, properties, initialUrl, servicesContainer);
};
}
+
+ @Override
+ public String getTopologyQuery() {
+ return TOPOLOGY_QUERY;
+ }
+
+ @Override
+ public String getInstanceIdQuery() {
+ return INSTANCE_ID_QUERY;
+ }
+
+ @Override
+ public String getIsReaderQuery() {
+ return IS_READER_QUERY;
+ }
+
+ @Override
+ public String getWriterIdQuery() {
+ return WRITER_ID_QUERY;
+ }
+
+ @Override
+ public String getWriterIdColumnName() {
+ return WRITER_ID_QUERY_COLUMN_NAME;
+ }
}
diff --git a/wrapper/src/main/java/software/amazon/jdbc/dialect/MysqlDialect.java b/wrapper/src/main/java/software/amazon/jdbc/dialect/MysqlDialect.java
index 7c930bb5e..e21bd06d5 100644
--- a/wrapper/src/main/java/software/amazon/jdbc/dialect/MysqlDialect.java
+++ b/wrapper/src/main/java/software/amazon/jdbc/dialect/MysqlDialect.java
@@ -33,46 +33,24 @@
public class MysqlDialect implements Dialect {
+ protected static final String VERSION_QUERY = "SHOW VARIABLES LIKE 'version_comment'";
+ protected static final String HOST_ALIAS_QUERY = "SELECT CONCAT(@@hostname, ':', @@port)";
+ private static MySQLExceptionHandler mySQLExceptionHandler;
+ private static final EnumSet NO_FAILOVER_RESTRICTIONS =
+ EnumSet.noneOf(FailoverRestriction.class);
private static final List dialectUpdateCandidates = Arrays.asList(
DialectCodes.GLOBAL_AURORA_MYSQL,
DialectCodes.AURORA_MYSQL,
DialectCodes.RDS_MULTI_AZ_MYSQL_CLUSTER,
DialectCodes.RDS_MYSQL
);
- private static MySQLExceptionHandler mySQLExceptionHandler;
- private static final EnumSet NO_RESTRICTIONS = EnumSet.noneOf(FailoverRestriction.class);
-
- @Override
- public int getDefaultPort() {
- return 3306;
- }
-
- @Override
- public ExceptionHandler getExceptionHandler() {
- if (mySQLExceptionHandler == null) {
- mySQLExceptionHandler = new MySQLExceptionHandler();
- }
- return mySQLExceptionHandler;
- }
-
- @Override
- public String getHostAliasQuery() {
- return "SELECT CONCAT(@@hostname, ':', @@port)";
- }
-
- @Override
- public String getServerVersionQuery() {
- return "SHOW VARIABLES LIKE 'version_comment'";
- }
+ protected final DialectUtils dialectUtils = new DialectUtils();
@Override
public boolean isDialect(final Connection connection) {
- Statement stmt = null;
- ResultSet rs = null;
- try {
- stmt = connection.createStatement();
- rs = stmt.executeQuery(this.getServerVersionQuery());
+ try (Statement stmt = connection.createStatement();
+ ResultSet rs = stmt.executeQuery(VERSION_QUERY)) {
while (rs.next()) {
final String columnValue = rs.getString(2);
if (columnValue != null && columnValue.toLowerCase().contains("mysql")) {
@@ -80,32 +58,31 @@ public boolean isDialect(final Connection connection) {
}
}
} catch (final SQLException ex) {
- // ignore
- } finally {
- if (stmt != null) {
- try {
- stmt.close();
- } catch (SQLException ex) {
- // ignore
- }
- }
- if (rs != null) {
- try {
- rs.close();
- } catch (SQLException ex) {
- // ignore
- }
- }
+ return false;
}
+
return false;
}
+ @Override
+ public int getDefaultPort() {
+ return 3306;
+ }
+
@Override
public List getDialectUpdateCandidates() {
return dialectUpdateCandidates;
}
- public HostListProviderSupplier getHostListProvider() {
+ @Override
+ public ExceptionHandler getExceptionHandler() {
+ if (mySQLExceptionHandler == null) {
+ mySQLExceptionHandler = new MySQLExceptionHandler();
+ }
+ return mySQLExceptionHandler;
+ }
+
+ public HostListProviderSupplier getHostListProviderSupplier() {
return (properties, initialUrl, servicesContainer) ->
new ConnectionStringHostListProvider(properties, initialUrl, servicesContainer.getHostListProviderService());
}
@@ -118,6 +95,16 @@ public void prepareConnectProperties(
@Override
public EnumSet getFailoverRestrictions() {
- return NO_RESTRICTIONS;
+ return NO_FAILOVER_RESTRICTIONS;
+ }
+
+ @Override
+ public String getServerVersionQuery() {
+ return VERSION_QUERY;
+ }
+
+ @Override
+ public String getHostAliasQuery() {
+ return HOST_ALIAS_QUERY;
}
}
diff --git a/wrapper/src/main/java/software/amazon/jdbc/dialect/PgDialect.java b/wrapper/src/main/java/software/amazon/jdbc/dialect/PgDialect.java
index 40363b4da..abf33ee56 100644
--- a/wrapper/src/main/java/software/amazon/jdbc/dialect/PgDialect.java
+++ b/wrapper/src/main/java/software/amazon/jdbc/dialect/PgDialect.java
@@ -36,21 +36,37 @@
*/
public class PgDialect implements Dialect {
+ protected static final String PG_PROC_EXISTS_QUERY = "SELECT 1 FROM pg_catalog.pg_proc LIMIT 1";
+ protected static final String VERSION_QUERY = "SELECT 'version', pg_catalog.VERSION()";
+ protected static final String HOST_ALIAS_QUERY =
+ "SELECT pg_catalog.CONCAT(pg_catalog.inet_server_addr(), ':', pg_catalog.inet_server_port())";
+
+ private static PgExceptionHandler pgExceptionHandler;
+ private static final EnumSet NO_FAILOVER_RESTRICTIONS =
+ EnumSet.noneOf(FailoverRestriction.class);
private static final List dialectUpdateCandidates = Arrays.asList(
DialectCodes.GLOBAL_AURORA_PG,
DialectCodes.AURORA_PG,
DialectCodes.RDS_MULTI_AZ_PG_CLUSTER,
DialectCodes.RDS_PG);
- private static PgExceptionHandler pgExceptionHandler;
+ protected final DialectUtils dialectUtils = new DialectUtils();
- private static final EnumSet NO_RESTRICTIONS = EnumSet.noneOf(FailoverRestriction.class);
+ @Override
+ public boolean isDialect(final Connection connection) {
+ return dialectUtils.checkExistenceQueries(connection, PG_PROC_EXISTS_QUERY);
+ }
@Override
public int getDefaultPort() {
return 5432;
}
+ @Override
+ public List getDialectUpdateCandidates() {
+ return dialectUpdateCandidates;
+ }
+
@Override
public ExceptionHandler getExceptionHandler() {
if (pgExceptionHandler == null) {
@@ -60,53 +76,7 @@ public ExceptionHandler getExceptionHandler() {
}
@Override
- public String getHostAliasQuery() {
- return "SELECT pg_catalog.CONCAT(pg_catalog.inet_server_addr(), ':', pg_catalog.inet_server_port())";
- }
-
- @Override
- public String getServerVersionQuery() {
- return "SELECT 'version', pg_catalog.VERSION()";
- }
-
- @Override
- public boolean isDialect(final Connection connection) {
- Statement stmt = null;
- ResultSet rs = null;
- try {
- stmt = connection.createStatement();
- rs = stmt.executeQuery("SELECT 1 FROM pg_catalog.pg_proc LIMIT 1");
- if (rs.next()) {
- return true;
- }
- } catch (final SQLException ex) {
- // ignore
- } finally {
- if (stmt != null) {
- try {
- stmt.close();
- } catch (SQLException ex) {
- // ignore
- }
- }
- if (rs != null) {
- try {
- rs.close();
- } catch (SQLException ex) {
- // ignore
- }
- }
- }
- return false;
- }
-
- @Override
- public List getDialectUpdateCandidates() {
- return dialectUpdateCandidates;
- }
-
- @Override
- public HostListProviderSupplier getHostListProvider() {
+ public HostListProviderSupplier getHostListProviderSupplier() {
return (properties, initialUrl, servicesContainer) ->
new ConnectionStringHostListProvider(properties, initialUrl, servicesContainer.getHostListProviderService());
}
@@ -119,6 +89,16 @@ public void prepareConnectProperties(
@Override
public EnumSet getFailoverRestrictions() {
- return NO_RESTRICTIONS;
+ return NO_FAILOVER_RESTRICTIONS;
+ }
+
+ @Override
+ public String getServerVersionQuery() {
+ return VERSION_QUERY;
+ }
+
+ @Override
+ public String getHostAliasQuery() {
+ return HOST_ALIAS_QUERY;
}
}
diff --git a/wrapper/src/main/java/software/amazon/jdbc/dialect/RdsMysqlDialect.java b/wrapper/src/main/java/software/amazon/jdbc/dialect/RdsMysqlDialect.java
index b91f6a1e3..1e173c772 100644
--- a/wrapper/src/main/java/software/amazon/jdbc/dialect/RdsMysqlDialect.java
+++ b/wrapper/src/main/java/software/amazon/jdbc/dialect/RdsMysqlDialect.java
@@ -26,13 +26,13 @@
public class RdsMysqlDialect extends MysqlDialect implements BlueGreenDialect {
- private static final String BG_STATUS_QUERY =
- "SELECT * FROM mysql.rds_topology";
-
- private static final String TOPOLOGY_TABLE_EXIST_QUERY =
+ protected static final String REPORT_HOST_EXISTS_QUERY = "SHOW VARIABLES LIKE 'report_host'";
+ protected static final String TOPOLOGY_TABLE_EXISTS_QUERY =
"SELECT 1 AS tmp FROM information_schema.tables WHERE"
+ " table_schema = 'mysql' AND table_name = 'rds_topology'";
+ protected static final String BG_STATUS_QUERY = "SELECT * FROM mysql.rds_topology";
+
private static final List dialectUpdateCandidates = Arrays.asList(
DialectCodes.AURORA_MYSQL,
DialectCodes.GLOBAL_AURORA_MYSQL,
@@ -54,50 +54,34 @@ public boolean isDialect(final Connection connection) {
// | Variable_name | value |
// |-----------------|---------------------|
// | version_comment | Source distribution |
- // If super.idDialect returns true there is no need to check for RdsMysqlDialect.
+ // If super.isDialect returns true there is no need to check for RdsMysqlDialect.
return false;
}
- Statement stmt = null;
- ResultSet rs = null;
-
- try {
- stmt = connection.createStatement();
- rs = stmt.executeQuery(this.getServerVersionQuery());
- if (!rs.next()) {
- return false;
- }
- final String columnValue = rs.getString(2);
- if (!"Source distribution".equalsIgnoreCase(columnValue)) {
- return false;
- }
- rs.close();
- rs = stmt.executeQuery("SHOW VARIABLES LIKE 'report_host'");
- if (!rs.next()) {
- return false;
- }
- final String reportHost = rs.getString(2); // get variable value; expected empty value
- return StringUtils.isNullOrEmpty(reportHost);
+ try (Statement stmt = connection.createStatement()) {
+ try (ResultSet rs = stmt.executeQuery(VERSION_QUERY)) {
+ if (!rs.next()) {
+ return false;
+ }
- } catch (final SQLException ex) {
- // ignore
- } finally {
- if (stmt != null) {
- try {
- stmt.close();
- } catch (SQLException ex) {
- // ignore
+ final String columnValue = rs.getString(2);
+ if (!"Source distribution".equalsIgnoreCase(columnValue)) {
+ return false;
}
}
- if (rs != null) {
- try {
- rs.close();
- } catch (SQLException ex) {
- // ignore
+
+ try (ResultSet rs = stmt.executeQuery(REPORT_HOST_EXISTS_QUERY)) {
+ if (!rs.next()) {
+ return false;
}
+
+ final String reportHost = rs.getString(2); // An empty value is expected
+ return StringUtils.isNullOrEmpty(reportHost);
}
+
+ } catch (final SQLException ex) {
+ return false;
}
- return false;
}
@Override
@@ -106,19 +90,12 @@ public List getDialectUpdateCandidates() {
}
@Override
- public String getBlueGreenStatusQuery() {
- return BG_STATUS_QUERY;
+ public boolean isBlueGreenStatusAvailable(final Connection connection) {
+ return dialectUtils.checkExistenceQueries(connection, TOPOLOGY_TABLE_EXISTS_QUERY);
}
@Override
- public boolean isBlueGreenStatusAvailable(final Connection connection) {
- try {
- try (Statement statement = connection.createStatement();
- ResultSet rs = statement.executeQuery(TOPOLOGY_TABLE_EXIST_QUERY)) {
- return rs.next();
- }
- } catch (SQLException ex) {
- return false;
- }
+ public String getBlueGreenStatusQuery() {
+ return BG_STATUS_QUERY;
}
}
diff --git a/wrapper/src/main/java/software/amazon/jdbc/dialect/RdsPgDialect.java b/wrapper/src/main/java/software/amazon/jdbc/dialect/RdsPgDialect.java
index 33d2c480a..62a52d019 100644
--- a/wrapper/src/main/java/software/amazon/jdbc/dialect/RdsPgDialect.java
+++ b/wrapper/src/main/java/software/amazon/jdbc/dialect/RdsPgDialect.java
@@ -24,6 +24,7 @@
import java.util.List;
import java.util.logging.Logger;
import software.amazon.jdbc.util.DriverInfo;
+import software.amazon.jdbc.util.Messages;
/**
* Suitable for the following AWS PG configurations.
@@ -33,61 +34,42 @@
*/
public class RdsPgDialect extends PgDialect implements BlueGreenDialect {
- private static final Logger LOGGER = Logger.getLogger(RdsPgDialect.class.getName());
-
- private static final List dialectUpdateCandidates = Arrays.asList(
- DialectCodes.RDS_MULTI_AZ_PG_CLUSTER,
- DialectCodes.GLOBAL_AURORA_PG,
- DialectCodes.AURORA_PG);
-
- private static final String extensionsSql = "SELECT (setting LIKE '%rds_tools%') AS rds_tools, "
+ protected static final String EXTENSIONS_EXIST_SQL = "SELECT (setting LIKE '%rds_tools%') AS rds_tools, "
+ "(setting LIKE '%aurora_stat_utils%') AS aurora_stat_utils "
+ "FROM pg_catalog.pg_settings "
+ "WHERE name OPERATOR(pg_catalog.=) 'rds.extensions'";
+ protected static final String TOPOLOGY_TABLE_EXISTS_QUERY =
+ "SELECT 'rds_tools.show_topology'::regproc";
- private static final String BG_STATUS_QUERY =
+ protected static final String BG_STATUS_QUERY =
"SELECT * FROM rds_tools.show_topology('aws_jdbc_driver-" + DriverInfo.DRIVER_VERSION + "')";
- private static final String TOPOLOGY_TABLE_EXIST_QUERY =
- "SELECT 'rds_tools.show_topology'::regproc";
+ private static final Logger LOGGER = Logger.getLogger(RdsPgDialect.class.getName());
+ private static final List dialectUpdateCandidates = Arrays.asList(
+ DialectCodes.RDS_MULTI_AZ_PG_CLUSTER,
+ DialectCodes.GLOBAL_AURORA_PG,
+ DialectCodes.AURORA_PG);
@Override
public boolean isDialect(final Connection connection) {
if (!super.isDialect(connection)) {
return false;
}
- Statement stmt = null;
- ResultSet rs = null;
- try {
- stmt = connection.createStatement();
- rs = stmt.executeQuery(extensionsSql);
+ try (Statement stmt = connection.createStatement();
+ ResultSet rs = stmt.executeQuery(EXTENSIONS_EXIST_SQL)) {
while (rs.next()) {
final boolean rdsTools = rs.getBoolean("rds_tools");
final boolean auroraUtils = rs.getBoolean("aurora_stat_utils");
- LOGGER.finest(() -> String.format("rdsTools: %b, auroraUtils: %b", rdsTools, auroraUtils));
+ LOGGER.finest(Messages.get("RdsPgDialect.rdsToolsAuroraUtils", new Object[] {rdsTools, auroraUtils}));
if (rdsTools && !auroraUtils) {
return true;
}
}
} catch (final SQLException ex) {
- // ignore
- } finally {
- if (stmt != null) {
- try {
- stmt.close();
- } catch (SQLException ex) {
- // ignore
- }
- }
- if (rs != null) {
- try {
- rs.close();
- } catch (SQLException ex) {
- // ignore
- }
- }
+ return false;
}
+
return false;
}
@@ -97,19 +79,12 @@ public List getDialectUpdateCandidates() {
}
@Override
- public String getBlueGreenStatusQuery() {
- return BG_STATUS_QUERY;
+ public boolean isBlueGreenStatusAvailable(final Connection connection) {
+ return dialectUtils.checkExistenceQueries(connection, TOPOLOGY_TABLE_EXISTS_QUERY);
}
@Override
- public boolean isBlueGreenStatusAvailable(final Connection connection) {
- try {
- try (Statement statement = connection.createStatement();
- ResultSet rs = statement.executeQuery(TOPOLOGY_TABLE_EXIST_QUERY)) {
- return rs.next();
- }
- } catch (SQLException ex) {
- return false;
- }
+ public String getBlueGreenStatusQuery() {
+ return BG_STATUS_QUERY;
}
}
diff --git a/wrapper/src/main/java/software/amazon/jdbc/dialect/TopologyDialect.java b/wrapper/src/main/java/software/amazon/jdbc/dialect/TopologyDialect.java
new file mode 100644
index 000000000..e7aa0f4d2
--- /dev/null
+++ b/wrapper/src/main/java/software/amazon/jdbc/dialect/TopologyDialect.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package software.amazon.jdbc.dialect;
+
+public interface TopologyDialect extends Dialect {
+ String getTopologyQuery();
+
+ String getInstanceIdQuery();
+
+ String getWriterIdQuery();
+
+ String getIsReaderQuery();
+}
diff --git a/wrapper/src/main/java/software/amazon/jdbc/dialect/UnknownDialect.java b/wrapper/src/main/java/software/amazon/jdbc/dialect/UnknownDialect.java
index f9bd1e4a3..067261242 100644
--- a/wrapper/src/main/java/software/amazon/jdbc/dialect/UnknownDialect.java
+++ b/wrapper/src/main/java/software/amazon/jdbc/dialect/UnknownDialect.java
@@ -82,7 +82,7 @@ public List getDialectUpdateCandidates() {
}
@Override
- public HostListProviderSupplier getHostListProvider() {
+ public HostListProviderSupplier getHostListProviderSupplier() {
return (properties, initialUrl, servicesContainer) ->
new ConnectionStringHostListProvider(properties, initialUrl, servicesContainer.getHostListProviderService());
}
diff --git a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/AuroraGlobalDbHostListProvider.java b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/AuroraGlobalDbHostListProvider.java
deleted file mode 100644
index d85069323..000000000
--- a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/AuroraGlobalDbHostListProvider.java
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
- * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License").
- * You may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package software.amazon.jdbc.hostlistprovider;
-
-import java.sql.ResultSet;
-import java.sql.SQLException;
-import java.sql.Timestamp;
-import java.time.Instant;
-import java.util.Arrays;
-import java.util.Map;
-import java.util.Properties;
-import java.util.logging.Logger;
-import java.util.stream.Collectors;
-import software.amazon.jdbc.AwsWrapperProperty;
-import software.amazon.jdbc.HostListProviderService;
-import software.amazon.jdbc.HostSpec;
-import software.amazon.jdbc.HostSpecBuilder;
-import software.amazon.jdbc.PropertyDefinition;
-import software.amazon.jdbc.util.ConnectionUrlParser;
-import software.amazon.jdbc.util.FullServicesContainer;
-import software.amazon.jdbc.util.RdsUtils;
-import software.amazon.jdbc.util.StringUtils;
-
-public class AuroraGlobalDbHostListProvider extends AuroraHostListProvider {
-
- static final Logger LOGGER = Logger.getLogger(AuroraGlobalDbHostListProvider.class.getName());
-
- public static final AwsWrapperProperty GLOBAL_CLUSTER_INSTANCE_HOST_PATTERNS =
- new AwsWrapperProperty(
- "globalClusterInstanceHostPatterns",
- null,
- "Comma-separated list of the cluster instance DNS patterns that will be used to "
- + "build a complete instance endpoints. "
- + "A \"?\" character in these patterns should be used as a placeholder for cluster instance names. "
- + "This parameter is required for Global Aurora Databases. "
- + "Each region in the Global Aurora Database should be specified in the list.");
-
- protected final RdsUtils rdsUtils = new RdsUtils();
-
- protected Map globalClusterInstanceTemplateByAwsRegion;
-
- static {
- PropertyDefinition.registerPluginProperties(AuroraGlobalDbHostListProvider.class);
- }
-
- public AuroraGlobalDbHostListProvider(Properties properties, String originalUrl,
- final FullServicesContainer servicesContainer, String topologyQuery,
- String nodeIdQuery, String isReaderQuery) {
- super(properties, originalUrl, servicesContainer, topologyQuery, nodeIdQuery, isReaderQuery);
- }
-
- @Override
- protected void initSettings() throws SQLException {
- super.initSettings();
-
- String templates = GLOBAL_CLUSTER_INSTANCE_HOST_PATTERNS.getString(properties);
- if (StringUtils.isNullOrEmpty(templates)) {
- throw new SQLException("Parameter 'globalClusterInstanceHostPatterns' is required for Aurora Global Database.");
- }
-
- HostSpecBuilder hostSpecBuilder = this.hostListProviderService.getHostSpecBuilder();
- this.globalClusterInstanceTemplateByAwsRegion = Arrays.stream(templates.split(","))
- .map(x -> ConnectionUrlParser.parseHostPortPairWithRegionPrefix(x.trim(), () -> hostSpecBuilder))
- .collect(Collectors.toMap(
- k -> k.getValue1(),
- v -> {
- this.validateHostPatternSetting(v.getValue2().getHost());
- return v.getValue2();
- }));
- LOGGER.finest(() -> "Recognized GDB instance template patterns:\n"
- + this.globalClusterInstanceTemplateByAwsRegion.entrySet().stream()
- .map(x -> String.format("\t[%s] -> %s", x.getKey(), x.getValue().getHostAndPort()))
- .collect(Collectors.joining("\n"))
- );
- }
-
- @Override
- protected HostSpec createHost(final ResultSet resultSet) throws SQLException {
-
- // suggestedWriterNodeId is not used for Aurora Global Database clusters.
- // Topology query can detect a writer for itself.
-
- // According to the topology query the result set
- // should contain 4 columns: node ID, 1/0 (writer/reader), node lag in time (msec), AWS region.
- String hostName = resultSet.getString(1);
- final boolean isWriter = resultSet.getBoolean(2);
- final float nodeLag = resultSet.getFloat(3);
- final String awsRegion = resultSet.getString(4);
-
- // Calculate weight based on node lag in time and CPU utilization.
- final long weight = Math.round(nodeLag) * 100L;
-
- final HostSpec clusterInstanceTemplateForRegion = this.globalClusterInstanceTemplateByAwsRegion.get(awsRegion);
- if (clusterInstanceTemplateForRegion == null) {
- throw new SQLException("Can't find cluster template for region " + awsRegion);
- }
-
- return createHost(hostName, isWriter, weight, Timestamp.from(Instant.now()), clusterInstanceTemplateForRegion);
- }
-}
diff --git a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/AuroraHostListProvider.java b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/AuroraHostListProvider.java
deleted file mode 100644
index fc53f9e1d..000000000
--- a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/AuroraHostListProvider.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License").
- * You may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package software.amazon.jdbc.hostlistprovider;
-
-
-import java.util.Properties;
-import java.util.logging.Logger;
-import software.amazon.jdbc.util.FullServicesContainer;
-
-
-public class AuroraHostListProvider extends RdsHostListProvider {
-
- static final Logger LOGGER = Logger.getLogger(AuroraHostListProvider.class.getName());
-
- public AuroraHostListProvider(
- final Properties properties,
- final String originalUrl,
- final FullServicesContainer servicesContainer,
- final String topologyQuery,
- final String nodeIdQuery,
- final String isReaderQuery) {
- super(properties,
- originalUrl,
- servicesContainer,
- topologyQuery,
- nodeIdQuery,
- isReaderQuery);
- }
-}
diff --git a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/AuroraTopologyUtils.java b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/AuroraTopologyUtils.java
new file mode 100644
index 000000000..f62415ff9
--- /dev/null
+++ b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/AuroraTopologyUtils.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package software.amazon.jdbc.hostlistprovider;
+
+import java.sql.Connection;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.sql.Timestamp;
+import java.time.Instant;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.logging.Logger;
+import org.checkerframework.checker.nullness.qual.Nullable;
+import software.amazon.jdbc.HostSpec;
+import software.amazon.jdbc.HostSpecBuilder;
+import software.amazon.jdbc.dialect.TopologyDialect;
+import software.amazon.jdbc.util.Messages;
+import software.amazon.jdbc.util.StringUtils;
+
+public class AuroraTopologyUtils extends TopologyUtils {
+ private static final Logger LOGGER = Logger.getLogger(AuroraTopologyUtils.class.getName());
+
+ public AuroraTopologyUtils(TopologyDialect dialect, HostSpecBuilder hostSpecBuilder) {
+ super(dialect, hostSpecBuilder);
+ }
+
+ @Override
+ protected @Nullable List getHosts(
+ Connection conn, ResultSet rs, HostSpec initialHostSpec, HostSpec instanceTemplate) throws SQLException {
+ // Data in the result set is ordered by last update time, so the latest records are last.
+ // We add hosts to a map to ensure newer records are not overwritten by older ones.
+ Map hostsMap = new HashMap<>();
+ while (rs.next()) {
+ try {
+ HostSpec host = createHost(rs, initialHostSpec, instanceTemplate);
+ hostsMap.put(host.getHost(), host);
+ } catch (Exception e) {
+ LOGGER.finest(Messages.get("TopologyUtils.errorProcessingQueryResults", new Object[] {e.getMessage()}));
+ return null;
+ }
+ }
+
+ return new ArrayList<>(hostsMap.values());
+ }
+
+ @Override
+ public boolean isWriterInstance(final Connection connection) throws SQLException {
+ try (final Statement stmt = connection.createStatement()) {
+ try (final ResultSet rs = stmt.executeQuery(this.dialect.getWriterIdQuery())) {
+ if (rs.next()) {
+ return !StringUtils.isNullOrEmpty(rs.getString(1));
+ }
+ }
+ }
+
+ return false;
+ }
+
+ protected HostSpec createHost(ResultSet rs, HostSpec initialHostSpec, HostSpec instanceTemplate) throws SQLException {
+ // According to the topology query the result set should contain 4 columns:
+ // instance ID, 1/0 (writer/reader), CPU utilization, instance lag in time.
+ String hostName = rs.getString(1);
+ final boolean isWriter = rs.getBoolean(2);
+ final double cpuUtilization = rs.getDouble(3);
+ final double instanceLag = rs.getDouble(4);
+ Timestamp lastUpdateTime;
+ try {
+ lastUpdateTime = rs.getTimestamp(5);
+ } catch (Exception e) {
+ lastUpdateTime = Timestamp.from(Instant.now());
+ }
+
+ // Calculate weight based on instance lag in time and CPU utilization.
+ final long weight = Math.round(instanceLag) * 100L + Math.round(cpuUtilization);
+
+ return createHost(hostName, hostName, isWriter, weight, lastUpdateTime, initialHostSpec, instanceTemplate);
+ }
+}
diff --git a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/ConnectionStringHostListProvider.java b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/ConnectionStringHostListProvider.java
index 80f55bdad..426ea3963 100644
--- a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/ConnectionStringHostListProvider.java
+++ b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/ConnectionStringHostListProvider.java
@@ -25,7 +25,6 @@
import java.util.logging.Logger;
import org.checkerframework.checker.nullness.qual.NonNull;
import software.amazon.jdbc.AwsWrapperProperty;
-import software.amazon.jdbc.HostListProviderService;
import software.amazon.jdbc.HostRole;
import software.amazon.jdbc.HostSpec;
import software.amazon.jdbc.util.ConnectionUrlParser;
@@ -36,7 +35,6 @@ public class ConnectionStringHostListProvider implements StaticHostListProvider
private static final Logger LOGGER = Logger.getLogger(ConnectionStringHostListProvider.class.getName());
final List hostList = new ArrayList<>();
- Properties properties;
private boolean isInitialized = false;
private final boolean isSingleWriterConnectionString;
private final ConnectionUrlParser connectionUrlParser;
@@ -75,11 +73,12 @@ private void init() throws SQLException {
}
this.hostList.addAll(
this.connectionUrlParser.getHostsFromConnectionUrl(this.initialUrl, this.isSingleWriterConnectionString,
- () -> this.hostListProviderService.getHostSpecBuilder()));
+ this.hostListProviderService::getHostSpecBuilder));
if (this.hostList.isEmpty()) {
throw new SQLException(Messages.get("ConnectionStringHostListProvider.parsedListEmpty",
new Object[] {this.initialUrl}));
}
+
this.hostListProviderService.setInitialConnectionHostSpec(this.hostList.get(0));
this.isInitialized = true;
}
diff --git a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/DynamicHostListProvider.java b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/DynamicHostListProvider.java
index 451c047f3..09d321c41 100644
--- a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/DynamicHostListProvider.java
+++ b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/DynamicHostListProvider.java
@@ -16,9 +16,7 @@
package software.amazon.jdbc.hostlistprovider;
-import software.amazon.jdbc.HostListProvider;
-
-// A marker interface for providers that can fetch a host list, and it changes depending on database status
-// A good example of such provider would be DB cluster provider (Aurora DB clusters, patroni DB clusters, etc.)
-// where cluster topology (nodes, their roles, their statuses) changes over time.
+// A marker interface for providers that can fetch a host list reflecting the current database topology.
+// Examples include providers for Aurora or Multi-AZ clusters, where the cluster topology, status, and instance roles
+// change over time.
public interface DynamicHostListProvider extends HostListProvider { }
diff --git a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/GlobalAuroraHostListProvider.java b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/GlobalAuroraHostListProvider.java
new file mode 100644
index 000000000..682ebb31f
--- /dev/null
+++ b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/GlobalAuroraHostListProvider.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package software.amazon.jdbc.hostlistprovider;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.logging.Logger;
+import software.amazon.jdbc.AwsWrapperProperty;
+import software.amazon.jdbc.HostSpec;
+import software.amazon.jdbc.PropertyDefinition;
+import software.amazon.jdbc.util.FullServicesContainer;
+import software.amazon.jdbc.util.RdsUtils;
+
+public class GlobalAuroraHostListProvider extends RdsHostListProvider {
+
+ public static final AwsWrapperProperty GLOBAL_CLUSTER_INSTANCE_HOST_PATTERNS =
+ new AwsWrapperProperty(
+ "globalClusterInstanceHostPatterns",
+ null,
+ "Comma-separated list of the cluster instance DNS patterns that will be used to "
+ + "build a complete instance endpoints. "
+ + "A \"?\" character in these patterns should be used as a placeholder for cluster instance names. "
+ + "This parameter is required for Global Aurora Databases. "
+ + "Each region in the Global Aurora Database should be specified in the list.");
+
+ protected final RdsUtils rdsUtils = new RdsUtils();
+ protected final GlobalAuroraTopologyUtils topologyUtils;
+
+ protected Map instanceTemplatesByRegion;
+
+ static {
+ PropertyDefinition.registerPluginProperties(GlobalAuroraHostListProvider.class);
+ }
+
+ public GlobalAuroraHostListProvider(
+ GlobalAuroraTopologyUtils topologyUtils, Properties properties, String originalUrl,
+ FullServicesContainer servicesContainer) {
+ super(topologyUtils, properties, originalUrl, servicesContainer);
+ this.topologyUtils = topologyUtils;
+ }
+
+ @Override
+ protected void initSettings() throws SQLException {
+ super.initSettings();
+
+ String instanceTemplates = GLOBAL_CLUSTER_INSTANCE_HOST_PATTERNS.getString(properties);
+ this.instanceTemplatesByRegion =
+ this.topologyUtils.parseInstanceTemplates(instanceTemplates, this::validateHostPatternSetting);
+ }
+
+ @Override
+ protected List queryForTopology(final Connection conn) throws SQLException {
+ init();
+ return this.topologyUtils.queryForTopology(conn, this.initialHostSpec, this.instanceTemplatesByRegion);
+ }
+}
diff --git a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/GlobalAuroraTopologyUtils.java b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/GlobalAuroraTopologyUtils.java
new file mode 100644
index 000000000..4a5d5eeea
--- /dev/null
+++ b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/GlobalAuroraTopologyUtils.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package software.amazon.jdbc.hostlistprovider;
+
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.SQLSyntaxErrorException;
+import java.sql.Statement;
+import java.sql.Timestamp;
+import java.time.Instant;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Consumer;
+import java.util.logging.Logger;
+import java.util.stream.Collectors;
+import org.checkerframework.checker.nullness.qual.Nullable;
+import software.amazon.jdbc.HostSpec;
+import software.amazon.jdbc.HostSpecBuilder;
+import software.amazon.jdbc.dialect.GlobalAuroraTopologyDialect;
+import software.amazon.jdbc.util.ConnectionUrlParser;
+import software.amazon.jdbc.util.LogUtils;
+import software.amazon.jdbc.util.Messages;
+import software.amazon.jdbc.util.Pair;
+import software.amazon.jdbc.util.StringUtils;
+
+public class GlobalAuroraTopologyUtils extends AuroraTopologyUtils {
+ private static final Logger LOGGER = Logger.getLogger(GlobalAuroraTopologyUtils.class.getName());
+
+ protected final GlobalAuroraTopologyDialect dialect;
+
+ public GlobalAuroraTopologyUtils(GlobalAuroraTopologyDialect dialect, HostSpecBuilder hostSpecBuilder) {
+ super(dialect, hostSpecBuilder);
+ this.dialect = dialect;
+ }
+
+ public @Nullable List queryForTopology(
+ Connection conn, HostSpec initialHostSpec, Map instanceTemplatesByRegion)
+ throws SQLException {
+ int originalNetworkTimeout = setNetworkTimeout(conn);
+ try (final Statement stmt = conn.createStatement();
+ final ResultSet rs = stmt.executeQuery(this.dialect.getTopologyQuery())) {
+ if (rs.getMetaData().getColumnCount() == 0) {
+ // We expect at least 4 columns. Note that the server may return 0 columns if failover has occurred.
+ LOGGER.finest(Messages.get("TopologyUtils.unexpectedTopologyQueryColumnCount"));
+ return null;
+ }
+
+ return this.verifyWriter(this.getHosts(rs, initialHostSpec, instanceTemplatesByRegion));
+ } catch (final SQLSyntaxErrorException e) {
+ throw new SQLException(Messages.get("TopologyUtils.invalidQuery"), e);
+ } finally {
+ if (originalNetworkTimeout == 0 && !conn.isClosed()) {
+ conn.setNetworkTimeout(networkTimeoutExecutor, originalNetworkTimeout);
+ }
+ }
+ }
+
+ protected @Nullable List getHosts(
+ ResultSet rs, HostSpec initialHostSpec, Map instanceTemplatesByRegion) throws SQLException {
+ // Data in the result set is ordered by last update time, so the latest records are last.
+ // We add hosts to a map to ensure newer records are not overwritten by older ones.
+ Map hostsMap = new HashMap<>();
+ while (rs.next()) {
+ try {
+ HostSpec host = createHost(rs, initialHostSpec, instanceTemplatesByRegion);
+ hostsMap.put(host.getHost(), host);
+ } catch (Exception e) {
+ LOGGER.finest(Messages.get("TopologyUtils.errorProcessingQueryResults", new Object[] {e.getMessage()}));
+ return null;
+ }
+ }
+
+ return new ArrayList<>(hostsMap.values());
+ }
+
+ protected HostSpec createHost(
+ ResultSet rs, HostSpec initialHostSpec, Map instanceTemplatesByRegion)
+ throws SQLException {
+ // According to the topology query the result set should contain 4 columns:
+ // instance ID, 1/0 (writer/reader), node lag in time (msec), AWS region.
+ String hostName = rs.getString(1);
+ final boolean isWriter = rs.getBoolean(2);
+ final float nodeLag = rs.getFloat(3);
+ final String awsRegion = rs.getString(4);
+
+ // Calculate weight based on node lag in time and CPU utilization.
+ final long weight = Math.round(nodeLag) * 100L;
+
+ final HostSpec instanceTemplate = instanceTemplatesByRegion.get(awsRegion);
+ if (instanceTemplate == null) {
+ throw new SQLException(Messages.get(
+ "GlobalAuroraTopologyMonitor.cannotFindRegionTemplate", new Object[] {awsRegion}));
+ }
+
+ return createHost(
+ hostName, hostName, isWriter, weight, Timestamp.from(Instant.now()), initialHostSpec, instanceTemplate);
+ }
+
+ public @Nullable String getRegion(String instanceId, Connection conn) throws SQLException {
+ try (final PreparedStatement stmt = conn.prepareStatement(this.dialect.getRegionByInstanceIdQuery())) {
+ stmt.setString(1, instanceId);
+ try (final ResultSet rs = stmt.executeQuery()) {
+ if (rs.next()) {
+ String awsRegion = rs.getString(1);
+ return StringUtils.isNullOrEmpty(awsRegion) ? null : awsRegion;
+ }
+ }
+ }
+
+ return null;
+ }
+
+ public Map parseInstanceTemplates(String instanceTemplatesString, Consumer hostValidator)
+ throws SQLException {
+ if (StringUtils.isNullOrEmpty(instanceTemplatesString)) {
+ throw new SQLException(Messages.get("GlobalAuroraTopologyUtils.globalClusterInstanceHostPatternsRequired"));
+ }
+
+ Map instanceTemplates = Arrays.stream(instanceTemplatesString.split(","))
+ .map(x -> ConnectionUrlParser.parseHostPortPairWithRegionPrefix(x.trim(), () -> hostSpecBuilder))
+ .collect(Collectors.toMap(
+ Pair::getValue1,
+ v -> {
+ hostValidator.accept(v.getValue2().getHost());
+ return v.getValue2();
+ }));
+ LOGGER.finest(Messages.get(
+ "GlobalAuroraTopologyUtils.detectedGdbPatterns",
+ new Object[] {LogUtils.toLogString(instanceTemplates)}));
+
+ return instanceTemplates;
+ }
+}
diff --git a/wrapper/src/main/java/software/amazon/jdbc/HostListProvider.java b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/HostListProvider.java
similarity index 89%
rename from wrapper/src/main/java/software/amazon/jdbc/HostListProvider.java
rename to wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/HostListProvider.java
index 0aa93714a..206a35415 100644
--- a/wrapper/src/main/java/software/amazon/jdbc/HostListProvider.java
+++ b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/HostListProvider.java
@@ -14,11 +14,14 @@
* limitations under the License.
*/
-package software.amazon.jdbc;
+package software.amazon.jdbc.hostlistprovider;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;
+import org.checkerframework.checker.nullness.qual.Nullable;
+import software.amazon.jdbc.HostRole;
+import software.amazon.jdbc.HostSpec;
public interface HostListProvider {
@@ -40,6 +43,7 @@ public interface HostListProvider {
*/
HostRole getHostRole(Connection connection) throws SQLException;
+ @Nullable
HostSpec identifyConnection(Connection connection) throws SQLException;
String getClusterId() throws UnsupportedOperationException, SQLException;
diff --git a/wrapper/src/main/java/software/amazon/jdbc/HostListProviderService.java b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/HostListProviderService.java
similarity index 89%
rename from wrapper/src/main/java/software/amazon/jdbc/HostListProviderService.java
rename to wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/HostListProviderService.java
index b2f6b5353..0413cb423 100644
--- a/wrapper/src/main/java/software/amazon/jdbc/HostListProviderService.java
+++ b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/HostListProviderService.java
@@ -14,9 +14,11 @@
* limitations under the License.
*/
-package software.amazon.jdbc;
+package software.amazon.jdbc.hostlistprovider;
import java.sql.Connection;
+import software.amazon.jdbc.HostSpec;
+import software.amazon.jdbc.HostSpecBuilder;
import software.amazon.jdbc.dialect.Dialect;
public interface HostListProviderService {
diff --git a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/MultiAzTopologyUtils.java b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/MultiAzTopologyUtils.java
new file mode 100644
index 000000000..1e1045002
--- /dev/null
+++ b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/MultiAzTopologyUtils.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package software.amazon.jdbc.hostlistprovider;
+
+import java.sql.Connection;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.sql.Timestamp;
+import java.time.Instant;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.logging.Logger;
+import org.checkerframework.checker.nullness.qual.Nullable;
+import software.amazon.jdbc.HostSpec;
+import software.amazon.jdbc.HostSpecBuilder;
+import software.amazon.jdbc.dialect.MultiAzClusterDialect;
+import software.amazon.jdbc.util.Messages;
+import software.amazon.jdbc.util.StringUtils;
+
+public class MultiAzTopologyUtils extends TopologyUtils {
+ private static final Logger LOGGER = Logger.getLogger(MultiAzTopologyUtils.class.getName());
+
+ protected final MultiAzClusterDialect dialect;
+
+ public MultiAzTopologyUtils(MultiAzClusterDialect dialect, HostSpecBuilder hostSpecBuilder) {
+ super(dialect, hostSpecBuilder);
+ this.dialect = dialect;
+ }
+
+ @Override
+ protected @Nullable List