@shinyaz

Aurora Blue/Green in Practice — Does AWS JDBC Wrapper 3.2.0 Change Anything?

Table of Contents

Introduction

In Part 2 and the MySQL extra, I tested the AWS JDBC Wrapper 2.6.4 Blue/Green plugin. PostgreSQL achieved 0 failures, MySQL had 0–1 failures (timing-dependent).

After the tests, I discovered the latest version is 3.2.0 (released February 2026). Multiple Blue/Green plugin bug fixes landed between 2.6.4 and 3.2.0. This article predicts behavior changes from the CHANGELOG, then verifies with actual tests.

Desk Check: Predictions from the CHANGELOG

Blue/Green related changes between 2.6.4 and 3.2.0:

VersionChangePredicted Impact
2.6.5Fix BG metadata retrieval for PG after switchoverPG stability improvement
2.6.7Outdated topology during switchover leads to wrong failoverMySQL 1 FAIL may be resolved
2.6.7Ensure BG monitor instances are set up properlyMonitoring stability
3.0.0BG plugin refactoring, cached connection expirationConnection management improvement
3.0.0Restart BG monitors after switchover completionResource leak prevention
3.2.0Fix memory spiking in BG monitor Statement creationMemory improvement

Predictions

  • PostgreSQL: Already 0 FAIL across all 3 tests with 2.6.4, so no change expected
  • MySQL: The 2.6.7 fix for "outdated topology causing wrong failover" may resolve the 1 FAIL

3.0.0 Breaking Changes

The clusterId parameter became mandatory for multiple clusters in 3.0.0, but our single-cluster setup requires no code changes.

Test Environment

ItemValue
Regionap-northeast-1 (Tokyo)
PostgreSQLAurora PostgreSQL 16.9 → 17.6
MySQLAurora MySQL 3.08.0 → 3.12.0
Instance classdb.r6g.large
TopologyWriter × 1 + Reader × 1 (each engine)
VPCDefault VPC (3 AZs)
AWS JDBC Wrapper3.2.0 (changed from 2.6.4)
Test interval1 second, 400 queries

The only pom.xml change is the aws-advanced-jdbc-wrapper version from 2.6.4 to 3.2.0. No test code changes needed.

Test Procedure

Build PostgreSQL and MySQL clusters in parallel, then create Blue/Green deployments simultaneously.

Aurora cluster setup (PostgreSQL + MySQL in parallel)
Terminal
# Shared subnet group
aws rds create-db-subnet-group \
  --db-subnet-group-name bg-v3-subnet \
  --db-subnet-group-description "Subnet group for v3 test" \
  --subnet-ids '["subnet-xxxxx","subnet-yyyyy","subnet-zzzzz"]'
 
# PostgreSQL: parameter group + cluster
aws rds create-db-cluster-parameter-group \
  --db-cluster-parameter-group-name bg-v3-pg16-params \
  --db-parameter-group-family aurora-postgresql16 \
  --description "PG16 params for v3 test"
aws rds modify-db-cluster-parameter-group \
  --db-cluster-parameter-group-name bg-v3-pg16-params \
  --parameters "ParameterName=rds.logical_replication,ParameterValue=1,ApplyMethod=pending-reboot"
aws rds create-db-cluster \
  --db-cluster-identifier bg-v3-pg \
  --engine aurora-postgresql --engine-version 16.9 \
  --master-username postgres --master-user-password '<password>' \
  --db-subnet-group-name bg-v3-subnet \
  --db-cluster-parameter-group-name bg-v3-pg16-params \
  --storage-encrypted --no-deletion-protection
 
# MySQL: parameter group + cluster
aws rds create-db-cluster-parameter-group \
  --db-cluster-parameter-group-name bg-v3-mysql8-params \
  --db-parameter-group-family aurora-mysql8.0 \
  --description "MySQL params for v3 test"
aws rds modify-db-cluster-parameter-group \
  --db-cluster-parameter-group-name bg-v3-mysql8-params \
  --parameters "ParameterName=binlog_format,ParameterValue=ROW,ApplyMethod=pending-reboot"
aws rds create-db-cluster \
  --db-cluster-identifier bg-v3-mysql \
  --engine aurora-mysql --engine-version 8.0.mysql_aurora.3.08.0 \
  --master-username admin --master-user-password '<password>' \
  --db-subnet-group-name bg-v3-subnet \
  --db-cluster-parameter-group-name bg-v3-mysql8-params \
  --storage-encrypted --no-deletion-protection
 
# Create all 4 instances in parallel (publicly accessible)
for pair in "bg-v3-pg-writer:bg-v3-pg:aurora-postgresql" "bg-v3-pg-reader:bg-v3-pg:aurora-postgresql" \
            "bg-v3-mysql-writer:bg-v3-mysql:aurora-mysql" "bg-v3-mysql-reader:bg-v3-mysql:aurora-mysql"; do
  IFS=: read -r inst cluster engine <<< "$pair"
  aws rds create-db-instance \
    --db-instance-identifier "$inst" --db-cluster-identifier "$cluster" \
    --db-instance-class db.r6g.large --engine "$engine" \
    --publicly-accessible --no-auto-minor-version-upgrade &
done
wait
 
# Allow local IP access (PG: 5432, MySQL: 3306)
MY_IP=$(curl -s https://checkip.amazonaws.com)
SG_ID=$(aws rds describe-db-clusters --db-cluster-identifier bg-v3-pg \
  --query 'DBClusters[0].VpcSecurityGroups[0].VpcSecurityGroupId' --output text)
aws ec2 authorize-security-group-ingress --group-id "$SG_ID" \
  --ip-permissions "[{\"IpProtocol\":\"tcp\",\"FromPort\":5432,\"ToPort\":5432,\"IpRanges\":[{\"CidrIp\":\"${MY_IP}/32\"}]},{\"IpProtocol\":\"tcp\",\"FromPort\":3306,\"ToPort\":3306,\"IpRanges\":[{\"CidrIp\":\"${MY_IP}/32\"}]}]"
Blue/Green deployment creation (PostgreSQL + MySQL in parallel)
Terminal
# PostgreSQL: Green parameter group + Blue/Green deployment
aws rds create-db-cluster-parameter-group \
  --db-cluster-parameter-group-name bg-v3-pg17-params \
  --db-parameter-group-family aurora-postgresql17 \
  --description "PG17 params for v3 Green"
aws rds modify-db-cluster-parameter-group \
  --db-cluster-parameter-group-name bg-v3-pg17-params \
  --parameters "ParameterName=rds.logical_replication,ParameterValue=1,ApplyMethod=pending-reboot"
aws rds create-blue-green-deployment \
  --blue-green-deployment-name bg-v3-pg-upgrade \
  --source arn:aws:rds:ap-northeast-1:<account-id>:cluster:bg-v3-pg \
  --target-engine-version 17.6 \
  --target-db-cluster-parameter-group-name bg-v3-pg17-params
 
# MySQL: Blue/Green deployment (no Green parameter group needed)
aws rds create-blue-green-deployment \
  --blue-green-deployment-name bg-v3-mysql-upgrade \
  --source arn:aws:rds:ap-northeast-1:<account-id>:cluster:bg-v3-mysql \
  --target-engine-version 8.0.mysql_aurora.3.12.0
pom.xml and project setup
Terminal
# Install Java 21 + Maven (Ubuntu)
sudo apt-get install -y openjdk-21-jdk maven
 
# Create project
mkdir -p bg-switchover-test/src/main/java/bgtest
cd bg-switchover-test
# Place pom.xml and SwitchoverTest.java (see below)

pom.xml (AWS JDBC Wrapper 3.2.0):

pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>bgtest</groupId>
    <artifactId>bg-switchover-test</artifactId>
    <version>1.0</version>
    <properties>
        <maven.compiler.source>21</maven.compiler.source>
        <maven.compiler.target>21</maven.compiler.target>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.postgresql</groupId>
            <artifactId>postgresql</artifactId>
            <version>42.7.5</version>
        </dependency>
        <dependency>
            <groupId>com.mysql</groupId>
            <artifactId>mysql-connector-j</artifactId>
            <version>9.2.0</version>
        </dependency>
        <dependency>
            <groupId>software.amazon.jdbc</groupId>
            <artifactId>aws-advanced-jdbc-wrapper</artifactId>
            <version>3.2.0</version>
            <exclusions>
                <exclusion>
                    <groupId>org.slf4j</groupId>
                    <artifactId>slf4j-api</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>com.zaxxer</groupId>
            <artifactId>HikariCP</artifactId>
            <version>6.2.1</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>2.0.16</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-simple</artifactId>
            <version>2.0.16</version>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-jar-plugin</artifactId>
                <version>3.4.2</version>
                <configuration>
                    <archive><manifest><mainClass>bgtest.SwitchoverTest</mainClass></manifest></archive>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-dependency-plugin</artifactId>
                <version>3.8.1</version>
                <executions>
                    <execution>
                        <id>copy-dependencies</id>
                        <phase>package</phase>
                        <goals><goal>copy-dependencies</goal></goals>
                        <configuration><outputDirectory>${project.build.directory}/lib</outputDirectory></configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>
SwitchoverTest.java (PostgreSQL)
SwitchoverTest.java
package bgtest;
 
import java.sql.*;
import java.time.Instant;
import java.time.Duration;
import java.util.Properties;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
 
public class SwitchoverTest {
    static final int CONNECT_TIMEOUT_SEC = 3;
    static final int QUERY_TIMEOUT_SEC = 3;
 
    public static void main(String[] args) throws Exception {
        if (args.length < 3) {
            System.err.println("Usage: SwitchoverTest <plain|hikari|wrapper> <endpoint> <password> [intervalMs] [maxQueries]");
            System.exit(1);
        }
        String mode = args[0], endpoint = args[1], password = args[2];
        int intervalMs = args.length > 3 ? Integer.parseInt(args[3]) : 1000;
        int maxQueries = args.length > 4 ? Integer.parseInt(args[4]) : 400;
 
        System.out.println("timestamp,query_num,status,latency_ms,server_ip,error");
        switch (mode) {
            case "plain"   -> runPlain(endpoint, password, intervalMs, maxQueries);
            case "wrapper" -> runWrapper(endpoint, password, intervalMs, maxQueries);
        }
    }
 
    static void runPlain(String ep, String pw, int interval, int max) throws Exception {
        String url = "jdbc:postgresql://" + ep + ":5432/postgres"
                + "?connectTimeout=" + CONNECT_TIMEOUT_SEC + "&socketTimeout=" + QUERY_TIMEOUT_SEC;
        int ok = 0, fail = 0;
        for (int n = 1; n <= max; n++) {
            Instant s = Instant.now();
            try (Connection c = DriverManager.getConnection(url, "postgres", pw);
                 Statement st = c.createStatement();
                 ResultSet r = st.executeQuery("SELECT inet_server_addr()::text")) {
                r.next(); long ms = Duration.between(s, Instant.now()).toMillis();
                System.out.println(Instant.now()+","+n+",OK,"+ms+","+r.getString(1)+","); ok++;
            } catch (Exception e) {
                long ms = Duration.between(s, Instant.now()).toMillis(); String err = e.getMessage().replace('\n',' ');
                System.out.println(Instant.now()+","+n+",FAIL,"+ms+",,"+err.substring(0,Math.min(100,err.length()))); fail++;
            }
            Thread.sleep(interval);
        }
        System.err.println("=== Plain JDBC Summary: OK="+ok+" FAIL="+fail+" Total="+max+" ===");
    }
 
    static void runWrapper(String ep, String pw, int interval, int max) throws Exception {
        String url = "jdbc:aws-wrapper:postgresql://"+ep+":5432/postgres";
        Properties p = new Properties();
        p.setProperty("user","postgres"); p.setProperty("password",pw);
        p.setProperty("wrapperPlugins","bg,failover2,efm2");
        p.setProperty("bgdId","bg-test-demo");
        p.setProperty("bgSwitchoverTimeoutMs","600000");
        p.setProperty("blue-green-monitoring-connectTimeout","20000");
        p.setProperty("blue-green-monitoring-socketTimeout","20000");
        p.setProperty("connectTimeout",String.valueOf(CONNECT_TIMEOUT_SEC));
        p.setProperty("socketTimeout",String.valueOf(QUERY_TIMEOUT_SEC));
        p.setProperty("wrapperLoggerLevel","fine");
        int ok = 0, fail = 0;
        for (int n = 1; n <= max; n++) {
            Instant s = Instant.now();
            try (Connection c = DriverManager.getConnection(url, p);
                 Statement st = c.createStatement();
                 ResultSet r = st.executeQuery("SELECT inet_server_addr()::text")) {
                r.next(); long ms = Duration.between(s, Instant.now()).toMillis();
                System.out.println(Instant.now()+","+n+",OK,"+ms+","+r.getString(1)+","); ok++;
            } catch (Exception e) {
                long ms = Duration.between(s, Instant.now()).toMillis(); String err = e.getMessage().replace('\n',' ');
                System.out.println(Instant.now()+","+n+",FAIL,"+ms+",,"+err.substring(0,Math.min(100,err.length()))); fail++;
            }
            Thread.sleep(interval);
        }
        System.err.println("=== Wrapper Summary: OK="+ok+" FAIL="+fail+" Total="+max+" ===");
    }
}
MysqlSwitchoverTest.java (MySQL)
MysqlSwitchoverTest.java
package bgtest;
 
import java.sql.*;
import java.time.Instant;
import java.time.Duration;
import java.util.Properties;
 
public class MysqlSwitchoverTest {
    static final int CONNECT_TIMEOUT_MS = 3000;
    static final int QUERY_TIMEOUT_SEC = 3;
    static final String QUERY = "SELECT @@hostname";
 
    public static void main(String[] args) throws Exception {
        if (args.length < 3) {
            System.err.println("Usage: MysqlSwitchoverTest <plain|wrapper> <endpoint> <password> [intervalMs] [maxQueries]");
            System.exit(1);
        }
        String mode = args[0], endpoint = args[1], password = args[2];
        int intervalMs = args.length > 3 ? Integer.parseInt(args[3]) : 1000;
        int maxQueries = args.length > 4 ? Integer.parseInt(args[4]) : 400;
 
        System.out.println("timestamp,query_num,status,latency_ms,server_host,error");
        switch (mode) {
            case "plain"   -> runPlain(endpoint, password, intervalMs, maxQueries);
            case "wrapper" -> runWrapper(endpoint, password, intervalMs, maxQueries);
        }
    }
 
    static void runPlain(String ep, String pw, int interval, int max) throws Exception {
        String url = "jdbc:mysql://" + ep + ":3306/mysql?connectTimeout=" + CONNECT_TIMEOUT_MS
                + "&socketTimeout=" + (QUERY_TIMEOUT_SEC * 1000);
        int ok = 0, fail = 0;
        for (int n = 1; n <= max; n++) {
            Instant s = Instant.now();
            try (Connection c = DriverManager.getConnection(url, "admin", pw);
                 Statement st = c.createStatement(); ResultSet r = st.executeQuery(QUERY)) {
                r.next(); long ms = Duration.between(s, Instant.now()).toMillis();
                System.out.println(Instant.now()+","+n+",OK,"+ms+","+r.getString(1)+","); ok++;
            } catch (Exception e) {
                long ms = Duration.between(s, Instant.now()).toMillis(); String err = e.getMessage().replace('\n',' ');
                System.out.println(Instant.now()+","+n+",FAIL,"+ms+",,"+err.substring(0,Math.min(100,err.length()))); fail++;
            }
            Thread.sleep(interval);
        }
        System.err.println("=== Plain: OK="+ok+" FAIL="+fail+" ===");
    }
 
    static void runWrapper(String ep, String pw, int interval, int max) throws Exception {
        String url = "jdbc:aws-wrapper:mysql://"+ep+":3306/mysql";
        Properties p = new Properties();
        p.setProperty("user","admin"); p.setProperty("password",pw);
        p.setProperty("wrapperPlugins","bg,failover2,efm2");
        p.setProperty("wrapperDialect","aurora-mysql");
        p.setProperty("bgdId","bg-mysql-demo");
        p.setProperty("bgSwitchoverTimeoutMs","600000");
        p.setProperty("blue-green-monitoring-connectTimeout","20000");
        p.setProperty("blue-green-monitoring-socketTimeout","20000");
        p.setProperty("connectTimeout",String.valueOf(CONNECT_TIMEOUT_MS));
        p.setProperty("socketTimeout",String.valueOf(QUERY_TIMEOUT_SEC*1000));
        p.setProperty("wrapperLoggerLevel","fine");
        int ok = 0, fail = 0;
        for (int n = 1; n <= max; n++) {
            Instant s = Instant.now();
            try (Connection c = DriverManager.getConnection(url, p);
                 Statement st = c.createStatement(); ResultSet r = st.executeQuery(QUERY)) {
                r.next(); long ms = Duration.between(s, Instant.now()).toMillis();
                System.out.println(Instant.now()+","+n+",OK,"+ms+","+r.getString(1)+","); ok++;
            } catch (Exception e) {
                long ms = Duration.between(s, Instant.now()).toMillis(); String err = e.getMessage().replace('\n',' ');
                System.out.println(Instant.now()+","+n+",FAIL,"+ms+",,"+err.substring(0,Math.min(100,err.length()))); fail++;
            }
            Thread.sleep(interval);
        }
        System.err.println("=== Wrapper: OK="+ok+" FAIL="+fail+" ===");
    }
}
Build and test execution
Terminal
# Build
mvn clean package -q
 
# Build classpath
CP="target/bg-switchover-test-1.0.jar"
for jar in target/lib/*.jar; do CP="$CP:$jar"; done
 
# Start Wrapper + Plain for both engines simultaneously
java -cp "$CP" bgtest.SwitchoverTest wrapper "$PG_ENDPOINT" '<password>' 1000 400 > v3-pg-wrapper.log 2>&1 &
java -cp "$CP" bgtest.SwitchoverTest plain  "$PG_ENDPOINT" '<password>' 1000 400 > v3-pg-plain.log 2>&1 &
java -cp "$CP" bgtest.MysqlSwitchoverTest wrapper "$MYSQL_ENDPOINT" '<password>' 1000 400 > v3-mysql-wrapper.log 2>&1 &
java -cp "$CP" bgtest.MysqlSwitchoverTest plain  "$MYSQL_ENDPOINT" '<password>' 1000 400 > v3-mysql-plain.log 2>&1 &
 
# Trigger switchovers (MySQL usually reaches AVAILABLE first)
aws rds switchover-blue-green-deployment --blue-green-deployment-identifier bgd-xxxxx --switchover-timeout 300
aws rds switchover-blue-green-deployment --blue-green-deployment-identifier bgd-yyyyy --switchover-timeout 300

Results

Both PostgreSQL and MySQL Blue/Green deployments were built in parallel, with Wrapper BG plugin and Plain JDBC running simultaneously for each switchover.

PostgreSQL: No Change as Predicted

Pattern2.6.43.2.0Change
Wrapper BG0 FAIL0 FAILNone
Plain JDBC8 FAIL8 FAILNone

The Wrapper recorded 0 failures out of 400 queries with 3.2.0, consistent with 2.6.4 results.

One observable difference: after switchover completion, the BG monitor automatically resets (3.0.0 PR #1639). On PostgreSQL, the status transitions to NOT_CREATED instead of COMPLETED. On MySQL, COMPLETED is logged before the reset. Both are improvements for consecutive Blue/Green switchovers and don't affect single switchover behavior.

MySQL: No Change — Prediction Was Wrong

Pattern2.6.43.2.0Change
Wrapper BG0–1 FAIL0–1 FAILNone
Plain JDBC6 FAIL6 FAILNone

The prediction was hard to verify. The 3.2.0 test showed 1 connection failure, but a subsequent reproduction test with the same steps showed 0 failures. MySQL's connection failure is timing-dependent during switchover, making it impossible to distinguish between version differences and timing differences.

Output (MySQL Wrapper 3.2.0)
09:53:46.061  #33  FAIL  3408ms  ← The active SQL connection has changed due to a connection failure

The error message is identical to 2.6.4. This error comes from the failover2 plugin detecting a connection drop — not from the BG plugin. It's a structural behavior of MySQL's failover detection mechanism, not a bug that can be fixed by BG plugin improvements.

2.6.4 vs 3.2.0 Comparison

Metric2.6.43.2.0
PG Wrapper FAIL00
PG Plain FAIL88
MySQL Wrapper FAIL0–10–1
MySQL Plain FAIL66
BG monitor auto-resetNoYes (3.0.0+)
Memory spike fixNoYes (3.2.0)
Consecutive switchover supportIncompleteImproved

Summary

  • Switchover failure counts are comparable between 2.6.4 and 3.2.0 — PostgreSQL stays at 0 FAIL, MySQL shows 0–1 FAIL (timing-dependent). The CHANGELOG bug fixes didn't produce a clear improvement in switchover failure counts.
  • MySQL's connection failure is a timing-dependent failover2 plugin behavior — Not fixable by BG plugin improvements. It doesn't occur every time, but a single retry in the application is recommended for safety.
  • 3.x migration is "recommended but not urgent" — Switchover failure counts don't change, but memory spike fixes and monitor auto-reset improve long-running stability. Especially beneficial for operations with consecutive Blue/Green switchovers.
  • CHANGELOGs alone can't accurately predict behavior changes — I predicted the "outdated topology" fix would resolve MySQL's connection failure, but the failure turned out to be timing-dependent, making it impossible to distinguish version differences from timing differences. This reinforces the importance of real-world testing and running multiple trials.

Cleanup

Resource deletion commands
Terminal
# Delete Blue/Green deployments
aws rds delete-blue-green-deployment --blue-green-deployment-identifier bgd-xxxxx
aws rds delete-blue-green-deployment --blue-green-deployment-identifier bgd-yyyyy
 
# Delete all instances (PG + MySQL, including old1)
for inst in bg-v3-pg-writer bg-v3-pg-reader bg-v3-pg-writer-old1 bg-v3-pg-reader-old1 \
            bg-v3-mysql-writer bg-v3-mysql-reader bg-v3-mysql-writer-old1 bg-v3-mysql-reader-old1; do
  aws rds delete-db-instance --db-instance-identifier "$inst" --skip-final-snapshot 2>/dev/null
done
 
# Delete clusters
for cluster in bg-v3-pg bg-v3-pg-old1 bg-v3-mysql bg-v3-mysql-old1; do
  aws rds delete-db-cluster --db-cluster-identifier "$cluster" --skip-final-snapshot 2>/dev/null
done
 
# Delete parameter groups and subnet group
aws rds delete-db-cluster-parameter-group --db-cluster-parameter-group-name bg-v3-pg16-params
aws rds delete-db-cluster-parameter-group --db-cluster-parameter-group-name bg-v3-pg17-params
aws rds delete-db-cluster-parameter-group --db-cluster-parameter-group-name bg-v3-mysql8-params
aws rds delete-db-subnet-group --db-subnet-group-name bg-v3-subnet

Share this post

Shinya Tahara

Shinya Tahara

Solutions Architect @ AWS

I'm a Solutions Architect at AWS, providing technical guidance primarily to financial industry customers. I share learnings about cloud architecture and AI/ML on this site.The views and opinions expressed on this site are my own and do not represent the official positions of my employer.

Related Posts