ちょっと話題の記事

PostgreSQL 互換 Amazon Auroraの高速フェイルオーバーを試してみた

2017.11.06

ウィスキー、シガー、パイプをこよなく愛する大栗です。

先日正式リリースとなったPostgreSQL 互換 Amazon Auroraですが、Auroraの特徴の一つである高速なフェイルオーバーを試してみました。

高速フェイルオーバー

ドキュメントにPostgreSQL互換Auroraの高速フェイルオーバーについて記述があります。PostgreSQL JDBC Driverを使用します。urlを複数記述して、targetServerTypeでmasterを指定するとWriterを高速にフェイルオーバーできます。

注意点として、targetServerTypeの指定ではコネクションのオープン時に判断されます。MySQL互換Auroraで使用されるMariaDB Connector/Jの場合はステートメントで判断しています。同じAurora用のフェイルオーバーでも挙動が異なるので注意して下さい。

MariaDB Connector/JによるAmazon RDS for Auroraの高速フェイルオーバー

試してみる

Auroraはオレゴンでr4.largeを2台構成で起動しています。

Amazon Linux AMI 2017.09.0(ami-e689729e)を使用しています。Amazon Linux 2017.09に初期インストールされているのはJava7です。

$ java -version
java version "1.7.0_151"
OpenJDK Runtime Environment (amzn-2.6.11.0.74.amzn1-x86_64 u151-b00)
OpenJDK 64-Bit Server VM (build 24.151-b00, mixed mode)

javacをインストールします。

$ sudo yum install java-1.7.0-openjdk-devel -y

PostgreSQL JDBC DriverのVersion 42.1.4をダウンロードしてCLASSPATHを通します。

$ wget https://jdbc.postgresql.org/download/postgresql-42.1.4.jre7.jar
$ sudo mv ./postgresql-42.1.4.jre7.jar /usr/share/java/
$ export CLASSPATH=/usr/share/java/postgresql-42.1.4.jre7.jar:.

簡単なサンプルコードを用意します。PostgreSQL JDBC DriverはMasterの判断をコネクションの接続時に行うため、1秒ごとにコネクションを作成してAuroraに接続します。

import java.sql.*;

class AuroraSample {
    public static void main(String[] args) {
        Connection conn = null;

        while(true) {
            try {
                Class.forName("org.postgresql.Driver");
                conn = DriverManager.getConnection(
                    "jdbc:postgresql://"
                    + "pg-aurora-1.a1b2c3d4e5f6.us-west-2.rds.amazonaws.com:5432,"
                    + "pg-aurora-2.a1b2c3d4e5f6.us-west-2.rds.amazonaws.com:5432"
                    + "/mydb?user=awsuser&password=mypassword&loginTimeout=2"
                    + "&connectTimeout=2&cancelSignalTimeout=2&socketTimeout=3"
                    + "&tcpKeepAlive=true&targetServerType=master&loadBalanceHosts=true"
                );

                Statement stmt = conn.createStatement();
                System.out.print(new java.util.Date());
                ResultSet rset = stmt.executeQuery("select inet_server_addr(), inet_server_port()");
                while ( rset.next() ) {
                    System.out.println("\t" + rset.getString(1) + "\t" + rset.getString(2));
                }
                rset.close();
                stmt.close();
                conn.close();
            } catch (SQLException e) {
               e.printStackTrace();
            } catch (Exception e) {
               e.printStackTrace();
            }

            try {
               Thread.sleep(1000);
            } catch (Exception e) {
               e.printStackTrace();
            }
        }
    }
}

ドキュメントに記載のあるカーネルパラメータの推奨設定をします。

$ sudo sysctl net.ipv4.tcp_keepalive_time=1
$ sudo sysctl net.ipv4.tcp_keepalive_intvl=1
$ sudo sysctl net.ipv4.tcp_keepalive_probes=5

コンパイルして実行してみます。途中でAuroraのフェイルオーバーを実行すると、2回(2秒)コネクションの接続が失敗して3秒後に別インスタンスに接続できています。何度かフェイルオーバーを試してみましたが、3~6秒程度で別インスタンスへアクセスできました。

$ javac ./pg_aurora.java
$ java AuroraSample -Dnetworkaddress.cache.ttl=1 -Dnetworkaddress.cache.negative.ttl=3
Sat Nov 04 14:47:42 UTC 2017	172.31.3.192	5432
Sat Nov 04 14:47:43 UTC 2017	172.31.3.192	5432
Sat Nov 04 14:47:44 UTC 2017	172.31.3.192	5432
Sat Nov 04 14:47:45 UTC 2017	172.31.3.192	5432
Sat Nov 04 14:47:46 UTC 2017	172.31.3.192	5432
Sat Nov 04 14:47:47 UTC 2017	172.31.3.192	5432
Sat Nov 04 14:47:48 UTC 2017	172.31.3.192	5432
Sat Nov 04 14:47:49 UTC 2017	172.31.3.192	5432
Sat Nov 04 14:47:50 UTC 2017	172.31.3.192	5432
Sat Nov 04 14:47:51 UTC 2017	172.31.3.192	5432
Nov 04, 2017 2:47:53 PM org.postgresql.Driver connect
SEVERE: Connection error:
org.postgresql.util.PSQLException: The connection attempt failed.
	at org.postgresql.Driver$ConnectThread.getResult(Driver.java:401)
	at org.postgresql.Driver.connect(Driver.java:259)
	at java.sql.DriverManager.getConnection(DriverManager.java:571)
	at java.sql.DriverManager.getConnection(DriverManager.java:233)
	at AuroraSample.main(pg_aurora.java:10)
Caused by: java.io.EOFException
	at org.postgresql.core.PGStream.receiveChar(PGStream.java:290)
	at org.postgresql.core.v3.ConnectionFactoryImpl.doAuthentication(ConnectionFactoryImpl.java:418)
	at org.postgresql.core.v3.ConnectionFactoryImpl.openConnectionImpl(ConnectionFactoryImpl.java:222)
	at org.postgresql.core.ConnectionFactory.openConnection(ConnectionFactory.java:49)
	at org.postgresql.jdbc.PgConnection.<init>(PgConnection.java:194)
	at org.postgresql.Driver.makeConnection(Driver.java:450)
	at org.postgresql.Driver.access$100(Driver.java:60)
	at org.postgresql.Driver$ConnectThread.run(Driver.java:360)
	at java.lang.Thread.run(Thread.java:748)

org.postgresql.util.PSQLException: The connection attempt failed.
	at org.postgresql.Driver$ConnectThread.getResult(Driver.java:401)
	at org.postgresql.Driver.connect(Driver.java:259)
	at java.sql.DriverManager.getConnection(DriverManager.java:571)
	at java.sql.DriverManager.getConnection(DriverManager.java:233)
	at AuroraSample.main(pg_aurora.java:10)
Caused by: java.io.EOFException
	at org.postgresql.core.PGStream.receiveChar(PGStream.java:290)
	at org.postgresql.core.v3.ConnectionFactoryImpl.doAuthentication(ConnectionFactoryImpl.java:418)
	at org.postgresql.core.v3.ConnectionFactoryImpl.openConnectionImpl(ConnectionFactoryImpl.java:222)
	at org.postgresql.core.ConnectionFactory.openConnection(ConnectionFactory.java:49)
	at org.postgresql.jdbc.PgConnection.<init>(PgConnection.java:194)
	at org.postgresql.Driver.makeConnection(Driver.java:450)
	at org.postgresql.Driver.access$100(Driver.java:60)
	at org.postgresql.Driver$ConnectThread.run(Driver.java:360)
	at java.lang.Thread.run(Thread.java:748)
Sat Nov 04 14:47:54 UTC 2017	172.31.47.81	5432
Sat Nov 04 14:47:55 UTC 2017	172.31.47.81	5432
Sat Nov 04 14:47:56 UTC 2017	172.31.47.81	5432
Sat Nov 04 14:47:57 UTC 2017	172.31.47.81	5432
Sat Nov 04 14:47:58 UTC 2017	172.31.47.81	5432

さいごに

クラスタエンドポイントを使用してもDNSベースでアクセス先を切り替えてフェイルオーバーも行えますが、より高速な切り替えを行いたい場合には使用すると良いのではないでしょうか。