Aurora(PostgreSQL)クラスタ構成へのJDBC接続

# Aurora(PostgreSQL)クラスタ構成へのJDBC接続について

Aurora(PostgreSQL)ではマスター1台、リードレプリカ最大15台のクラスタ構成を構築することができます。
（当たり前ですが）マスターは読み書きの両方、リードレプリカは読み込みだけが実行できます。

マスターのみの構成であればJDBC接続のURLは以下のようになります。

```
jdbc:postgresql://database-1.cluster-XXX.ap-northeast-1.rds.amazonaws.com:5432/postgres
```

今回はマスター1台、リードレプリカ1台の以下の構成でJDBCでの接続を試してみます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179607/dbc8877b-68d1-f152-a145-7c416550c034.png)


# クラスタ構成へのJDBC接続方法

クラスタ構成への接続URLの設定例は以下のようになります。

```
jdbc:postgresql://database-1.cluster-XXXX.ap-northeast-1.rds.amazonaws.com:5432,database-1.cluster-ro-XXXX.ap-northeast-1.rds.amazonaws.com:5432/postgres?loginTimeout=2&connectTimeout=2&cancelSignalTimeout=2&socketTimeout=60&tcpKeepAlive=true&targetServerType=master&loadBalanceHosts=true
```

「database-1.cluster-XXXX.ap-northeast-1.rds.amazonaws.com:5432,database-1.cluster-ro-XXXX.ap-northeast-1.rds.amazonaws.com:5432」でマスターとリードレプリカのエンドポイントを指定しています。

マスターのエンドポイントはクラスターエンドポイント、もしくはライターエンドポイントと呼びます。
マスターのインスタンスは1台で、エンドポイントも1つになります。

一方、リードレプリカは1台から15台で、1つのエンドポイントに紐づきます。
リーダーエンドポイントに接続することで負荷分散されます。


指定するエンドポイントは、以下のようにコンソールから確認できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179607/decfcfe4-7773-d9e9-5fe0-1fca773f820a.png)


また、targetServerTypeは実際に接続するエンドポイントを選択します。主に使用するのは以下の3つだと思います。

- master：マスターに接続する
- slave：リードレプリカに接続する
- preferSlave：リードレプリカに接続するが、接続できない場合はマスターに接続する


「loginTimeout=2&connectTimeout=2&cancelSignalTimeout=2&socketTimeout=60&tcpKeepAlive=true」あたりはAuroraの推奨設定です。

以下は公式の参考ページです。

- [Connecting to the Database](https://jdbc.postgresql.org/documentation/head/connect.html)
- [Amazon Aurora PostgreSQL を使用する際のベストプラクティス](https://docs.aws.amazon.com/ja_jp/AmazonRDS/latest/AuroraUserGuide/AuroraPostgreSQL.BestPractices.html)


# フェイルオーバー

1秒ごとにINSERT文を発行して、マスターからリードレプリカへフェイルオーバーさせて、実行結果がどうなるかを確認してみました。DB接続はコネクションプーリングを利用しています。

フェイルオーバーを実行する場合は、以下の画面で「フェイルオーバー」を選択します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179607/691bb88c-f91b-a991-8931-f59736350128.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179607/385a24cc-18ce-a97d-3e3a-0f3db183bcae.png)


フェイルオーバー実行中は、5秒～10秒ぐらいINSERT文でエラーが発生しました。

```
Caused by: org.postgresql.util.PSQLException: バックエンドへの送信中に、入出力エラーが起こりました。
```

SELECT文をマスター、リードレプリカに接続した場合にフェイルオーバーした場合も同様に5秒～10秒ぐらいエラーになりました。


# その他


## 他のベストプラクティス設定

JDBC接続のベストプラクティスは上述のとおりですが、他にも高速フェイルオーバーのために以下の設定が推奨されていました。

アプリケーション

```
// Sets internal TTL to match the Aurora RO Endpoint TTL
java.security.Security.setProperty("networkaddress.cache.ttl" , "1");
// If the lookup fails, default to something like small to retry
java.security.Security.setProperty("networkaddress.cache.negative.ttl" , "3");
```

OS（Linux）

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


## リードレプリカに対してINSERT文を発行

リードレプリカに接続してINSERT文を発行すると以下のエラーが発生します。

```
org.postgresql.util.PSQLException: ERROR: cannot execute INSERT in a read-only transaction
```


## コネクションプーリング

リードレプリカが複数の場合にコネクションプーリングを使用すると、コネクション作成時に接続先のリードレプリカが決まります。そのため、コネクションは負荷分散されますが、実行されるSQLごとに分散されるわけではありません。
また、リードレプリカが再起動された場合は他のインスタンスにコネクションが再分散されます。インスタンスが起動後もコネクションは分散されないので、プーリングされたコネクションは再度負荷分散されるようにプーリングの保持期間を設定しておくのが良いと思います。


# 参考

- [Amazon Aurora PostgreSQL を使用する際のベストプラクティス](https://docs.aws.amazon.com/ja_jp/AmazonRDS/latest/AuroraUserGuide/AuroraPostgreSQL.BestPractices.html)
- [Connecting to the Database](https://jdbc.postgresql.org/documentation/head/connect.html)
- [Amazon Aurora DB クラスターへの接続](https://docs.aws.amazon.com/ja_jp/AmazonRDS/latest/AuroraUserGuide/Aurora.Connecting.html)
- [Amazon Aurora 接続管理](https://docs.aws.amazon.com/ja_jp/AmazonRDS/latest/AuroraUserGuide/Aurora.Overview.Endpoints.html)