Amazon Aurora PostgreSQL를 사용한 빠른 장애 조치 - Amazon Aurora

Amazon Aurora PostgreSQL를 사용한 빠른 장애 조치

다음에서는 장애 조치가 가능한 한 빨리 발생하도록 하는 방법을 배울 수 있습니다. 장애 조치 후 신속하게 복구하기 위해 Aurora PostgreSQL DB 클러스터에 대한 클러스터 캐시 관리를 사용할 수 있습니다. 자세한 내용은 장애 조치 후 Aurora PostgreSQL용 클러스터 캐시 관리를 통한 신속한 복구 섹션을 참조하세요.

장애 조치가 빠르게 이루어지도록 하기 위해 수행할 수 있는 몇 가지 단계는 다음과 같습니다.

  • 장애가 있는 경우 읽기 제한 시간이 만료되기 전에 더 긴 실행 쿼리를 중지하려면 짧은 시간 프레임으로 전송 제어 프로토콜(TCP) Keepalives를 설정합니다.

  • Java 도메인 이름 시스템(DNS) 캐싱에 대한 제한 시간을 적극적으로 설정합니다. 이를 통해 Aurora 읽기 전용 엔드포인트가 나중에 연결을 시도할 때 읽기 전용 노드를 적절히 순환할 수 있습니다.

  • JDBC 연결 문자열에 사용되는 제한 시간 변수를 가능한 낮게 설정합니다. 단기 및 장기 실행 중인 쿼리에 별도의 연결 객체를 사용합니다.

  • 읽기 및 쓰기 Aurora 엔드포인트를 사용하여 클러스터에 연결합니다.

  • RDS API 작업을 사용하여 서버 측 장애에 대한 애플리케이션 응답을 테스트합니다. 또한 패킷 삭제 도구를 사용하여 클라이언트 측 장애에 대한 애플리케이션 응답을 테스트합니다.

  • AWS JDBC Driver for PostgreSQL을 사용하여 Aurora PostgreSQL의 장애 조치 기능을 최대한 활용합니다. AWS JDBC Driver for PostgreSQL 및 사용 방법에 관한 자세한 내용은 AWS JDBC Driver for PostgreSQL GitHub 리포지토리를 참조하세요.

이에 대해서는 다음에 이어지는 섹션에서 자세히 설명합니다.

TCP Keepalives 파라미터 설정

TCP 연결을 설정하면 타이머 집합이 연결과 연결됩니다. keepalive 타이머가 0에 도달하면 keepalive 프로브 패킷을 연결 엔드포인트로 전송합니다. 프로브가 응답을 수신하면 연결이 계속 유지되는 것으로 가정할 수 있습니다.

TCP keepalive 파라미터를 활성화하고 적극적으로 설정하면 클라이언트가 더 이상 데이터베이스에 연결할 수 없을 경우 활성 연결이 빠르게 종료됩니다. 그러면 애플리케이션이 새 엔드포인트에 연결할 수 있습니다.

다음 TCP keepalive 파라미터를 설정해야 합니다.

  • tcp_keepalive_time은 시간을 초 단위로 제어하며, 이후 소켓으로부터 데이터가 전송되지 않을 경우 keepalive 패킷이 전송됩니다. ACK는 데이터로 간주되지 않습니다. 다음 설정을 권장합니다.

    tcp_keepalive_time = 1

  • tcp_keepalive_intvl은 초기 패킷이 전송된 후 후속 keepalive 패킷을 전송하는 시간 주기를 초 단위로 제어합니다. tcp_keepalive_time 파라미터를 사용하여 이 시간을 설정합니다. 다음 설정을 권장합니다.

    tcp_keepalive_intvl = 1

  • tcp_keepalive_probes는 애플리케이션에 알림이 전달되기 전 발생하는 승인되지 않은 keepalive 프로브의 개수입니다. 다음 설정을 권장합니다.

    tcp_keepalive_probes = 5

이러한 설정은 데이터베이스가 응답을 중단하고 5초 안에 애플리케이션에 알려야 합니다. keepalive 패킷이 애플리케이션의 네트워크 내에서 자주 삭제되는 경우 tcp_keepalive_probes 값을 높게 설정할 수 있습니다. 이렇게 하면 실제 장애를 탐지하는 데 걸리는 시간이 늘어나지만 덜 안정적인 네트워크에서 더 많은 버퍼를 허용합니다.

Linux에서 TCP keepalive 파라미터를 설정하려면
  1. TCP keepalive 파라미터가 어떻게 구성되어 있는지 테스트합니다.

    명령줄에서 다음 명령을 사용하여 이 작업을 수행하는 것이 좋습니다. 이 권장 구성은 시스템 전체에 적용됩니다. 즉, SO_KEEPALIVE 옵션이 켜진 상태에서 소켓을 생성하는 다른 모든 애플리케이션에도 영향을 미칩니다.

    sudo sysctl net.ipv4.tcp_keepalive_time=1 sudo sysctl net.ipv4.tcp_keepalive_intvl=1 sudo sysctl net.ipv4.tcp_keepalive_probes=5
  2. 애플리케이션에 적합한 구성을 찾은 경우, 사용자가 변경한 내용을 포함해 /etc/sysctl.conf에 다음 줄을 추가하여 이러한 설정을 유지합니다.

    tcp_keepalive_time = 1 tcp_keepalive_intvl = 1 tcp_keepalive_probes = 5

애플리케이션에 빠른 장애 조치 구성

다음에서는 빠른 장애 조치를 위해 수행할 수 있는 Aurora PostgreSQL의 여러 구성 변경 사항에 대한 설명을 찾을 수 있습니다. PostgreSQL JDBC 드라이버 설정 및 구성에 대한 자세한 내용은 PostgreSQL JDBC Driver를 방문하여 확인하세요.

DNS 캐시 제한 시간 축소

애플리케이션이 장애 조치 이후 연결을 설정할 때는 이전 리더가 새로운 Aurora PostgreSQL 라이터로 사용됩니다. 이전 리더는 DNS 업데이트가 완전히 전파되기 전에 Aurora 읽기 전용 엔드포인트를 사용해 찾을 수 있습니다. java DNS 유지 시간(TTL) 값을 30초 미만과 같이 낮게 설정하면 후속 연결 시도 시 리더 노드 사이에 순환이 이루어집니다.

// 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");

빠른 장애 조치를 위한 Aurora PostgreSQL 연결 문자열 설정

Aurora PostgreSQL 빠른 장애 조치를 사용하려면 애플리케이션의 연결 문자열에 단일 호스트가 아닌 호스트 목록이 있어야 합니다. 다음은 Aurora PostgreSQL 클러스터에 연결하는 데 사용할 수 있는 연결 문자열의 예입니다. 이 예에서는 호스트가 굵게 표시됩니다.

jdbc:postgresql://myauroracluster.cluster-c9bfei4hjlrd.us-east-1-beta.rds.amazonaws.com:5432, myauroracluster.cluster-ro-c9bfei4hjlrd.us-east-1-beta.rds.amazonaws.com:5432 /postgres?user=<primaryuser>&password=<primarypw>&loginTimeout=2 &connectTimeout=2&cancelSignalTimeout=2&socketTimeout=60 &tcpKeepAlive=true&targetServerType=primary

최상의 가용성과 RDS API에 대한 종속성을 피하기 위해 연결할 파일을 유지 관리하는 것이 좋습니다. 이 파일에는 데이터베이스에 연결할 때 애플리케이션이 읽는 호스트 문자열이 포함되어 있습니다. 이 호스트 문자열에는 클러스터에 제공된 모든 Aurora 엔드포인트가 있습니다. Aurora 엔드포인트에 대한 자세한 내용은 Amazon Aurora 연결 관리 섹션을 참조하세요.

예를 들어, 다음과 같이 로컬 파일에 엔드포인트를 저장할 수 있습니다.

myauroracluster.cluster-c9bfei4hjlrd.us-east-1-beta.rds.amazonaws.com:5432, myauroracluster.cluster-ro-c9bfei4hjlrd.us-east-1-beta.rds.amazonaws.com:5432

애플리케이션이 이 파일에서 읽어 들여 JDBC 연결 문자열의 호스트 섹션을 채웁니다. DB 클러스터 이름을 바꾸면 이러한 엔드포인트가 변경됩니다. 이벤트가 발생할 경우 애플리케이션이 이 이벤트를 처리하는지 확인하세요.

다른 방법은 다음과 같이 DB 인스턴스 노드 목록을 사용하는 것입니다.

my-node1.cksc6xlmwcyw.us-east-1-beta.rds.amazonaws.com:5432, my-node2.cksc6xlmwcyw.us-east-1-beta.rds.amazonaws.com:5432, my-node3.cksc6xlmwcyw.us-east-1-beta.rds.amazonaws.com:5432, my-node4.cksc6xlmwcyw.us-east-1-beta.rds.amazonaws.com:5432

이 방식의 장점은 PostgreSQL JDBC 연결 드라이버가 이 목록에서 모든 노드를 통해 순환하며 유효한 연결을 찾는다는 것입니다. 이와는 반대로 Aurora 엔드포인트를 사용하는 경우 각 연결 시도에서 두 개의 노드만 시도됩니다. 하지만 DB 인스턴스 노드를 사용하는 데에는 단점이 있습니다. 사용자가 클러스터에 노드를 추가하거나 제거하고 인스턴스 엔드포인트 목록이 무효로 되면 연결 드라이버가 연결할 올바른 호스트를 찾을 수 없다는 것입니다.

애플리케이션이 한 호스트에 연결하기 위해 너무 오래 기다리지 않도록 하려면 다음 파라미터를 적극적으로 설정합니다.

  • targetServerType – 드라이버가 쓰기 노드에 연결되는지 아니면 읽기 노드에 연결되는지를 제어합니다. 애플리케이션이 쓰기 노드에만 다시 연결되도록 하려면 targetServerType 값을 primary로 설정합니다.

    targetServerType 파라미터에 사용할 수 있는 값에는 primary, secondary, any, preferSecondary가 있습니다. 이 preferSecondary 값은 먼저 리더와의 연결 설정을 시도합니다. 리더 연결을 설정할 수 없는 경우 라이터에 연결합니다.

  • loginTimeout – 소켓 연결이 설정된 후 애플리케이션이 데이터베이스에 로그인하기까지 대기하는 시간을 제어합니다.

  • connectTimeout – 소켓이 데이터베이스에 연결을 설정하기까지 대기하는 시간을 제어합니다.

애플리케이션을 얼마나 적극적으로 설정하고 싶은지에 따라, 다른 애플리케이션 파라미터를 수정하여 연결 프로세스의 속도를 높일 수 있습니다.

  • cancelSignalTimeout – 일부 애플리케이션에서는 시간 초과 쿼리에 "최대한" 취소 신호를 보내야 할 수 있습니다. 이 취소 신호가 장애 조치 경로에 있는 경우, 잘못된 호스트로 이 신호가 전달되지 않도록 적극적으로 설정하는 것이 좋습니다.

  • socketTimeout – 이 파라미터는 소켓이 읽기 작업을 대기하는 시간을 제어합니다. 쿼리가 이 값보다 길게 대기하지 않도록 이 파라미터를 글로벌 "쿼리 제한 시간"으로 사용할 수 있습니다. 연결 핸들러를 두 개 사용하는 것이 좋습니다. 하나의 연결 핸들러는 수명이 짧은 쿼리를 실행하고 이 값을 낮게 설정합니다. 장기 실행 쿼리를 위한 또 다른 연결 핸들러에서는 이 값이 훨씬 더 높게 설정되어 있습니다. 이 방식으로는 서버가 다운될 경우 TCP keepalive 파라미터를 이용해 장기 실행 쿼리를 중단할 수 있습니다.

  • tcpKeepAlive – 이 파라미터를 켜면 사용자가 설정한 TCP keepalive 파라미터가 적용되도록 합니다.

  • loadBalanceHosts – 이 파라미터를 true로 설정하면 애플리케이션을 후보 호스트 목록에서 선택한 임의의 호스트에 연결합니다.

호스트 문자열을 가져올 수 있는 다른 옵션

aurora_replica_status 함수 및 Amazon RDS API 사용을 비롯해 여러 소스로부터 호스트 문자열을 가져올 수 있습니다.

대부분의 경우 클러스터 라이터를 확인하거나 클러스터의 다른 리더 노드를 찾아야 합니다. 이를 위해 애플리케이션에서 DB 클러스터의 DB 인스턴스에 연결하고 aurora_replica_status 함수를 쿼리할 수 있습니다. 이 함수를 사용하면 연결할 호스트를 찾는 데 걸리는 시간을 줄일 수 있습니다. 그러나 특정 네트워크 장애 시나리오에서는 aurora_replica_status 기능이 오래되었거나 불완전한 정보를 표시할 수 있습니다.

애플리케이션이 연결할 노드를 찾도록 보장하는 좋은 방법 중 하나는클러스터 라이터 엔드포인트에 연결한 다음 클러스터 리더 엔드포인트에 연결을 시도하는 것입니다. 읽기 가능한 연결을 설정할 때까지 이 작업을 수행합니다. 이러한 엔드포인트는 DB 클러스터의 이름을 변경하지 않는 한 변경되지 않습니다. 따라서 일반적으로 이들을 애플리케이션의 정적 멤버로 남겨두거나 애플리케이션이 읽는 리소스 파일에 저장할 수 있습니다.

이러한 엔드포인트 중 하나를 사용하여 연결을 설정한 후 클러스터의 나머지 부분에 대한 정보를 얻을 수 있습니다. 이렇게 하려면 aurora_replica_status 함수를 호출합니다. 예를 들어 다음 명령은 aurora_replica_status로 정보를 검색합니다.

postgres=> SELECT server_id, session_id, highest_lsn_rcvd, cur_replay_latency_in_usec, now(), last_update_timestamp FROM aurora_replica_status(); server_id | session_id | highest_lsn_rcvd | cur_replay_latency_in_usec | now | last_update_timestamp -----------+--------------------------------------+------------------+----------------------------+-------------------------------+------------------------ mynode-1 | 3e3c5044-02e2-11e7-b70d-95172646d6ca | 594221001 | 201421 | 2017-03-07 19:50:24.695322+00 | 2017-03-07 19:50:23+00 mynode-2 | 1efdd188-02e4-11e7-becd-f12d7c88a28a | 594221001 | 201350 | 2017-03-07 19:50:24.695322+00 | 2017-03-07 19:50:23+00 mynode-3 | MASTER_SESSION_ID | | | 2017-03-07 19:50:24.695322+00 | 2017-03-07 19:50:23+00 (3 rows)

이를테면 연결 문자열의 호스트 섹션은 라이터와 리더 클러스터 엔드포인트 모두로 시작할 수 있습니다.

myauroracluster.cluster-c9bfei4hjlrd.us-east-1-beta.rds.amazonaws.com:5432, myauroracluster.cluster-ro-c9bfei4hjlrd.us-east-1-beta.rds.amazonaws.com:5432

이 시나리오에서 애플리케이션은 기본 또는 보조 노드 유형에 연결을 설정하려 할 것입니다. 애플리케이션이 연결되면 먼저 명령 의 결과를 쿼리하여 노드의 읽기/쓰기 상태를 검사하는 것이 좋습니다. 이렇게 하려면 SHOW transaction_read_only 명령 결과를 쿼리해야 합니다.

쿼리의 반환 값이 OFF인 경우 프라이머리 노드에 성공적으로 연결된 것입니다. 그러나 반환 값이 ON이고 애플리케이션에 읽기/쓰기 연결이 필요하다고 가정합니다. 이 경우 aurora_replica_status 함수를 호출하여 session_id='MASTER_SESSION_ID'server_id를 확인할 수 있습니다. 이 함수는 프라이머리 노드의 이름을 반환합니다. 다음에 설명하는 endpointPostfix와 함께 사용할 수 있습니다.

단, 오래된 데이터가 있는 복제본에 연결하는 경우에는 주의해야 합니다. 이 경우 aurora_replica_status 함수에 오래된 정보가 표시될 수 있습니다. 애플리케이션 수준에서 부실 임계값을 설정할 수 있습니다. 이를 확인하기 위해 서버 시간과 last_update_timestamp 값의 차이를 확인할 수 있습니다. 일반적으로 애플리케이션은 aurora_replica_status 함수에 의해 반환된 충돌 정보로 인한 두 호스트 사이의 대칭 이동을 방지해야 합니다. 애플리케이션은 aurora_replica_status에서 반환된 데이터를 따르는 대신 알려진 모든 호스트를 먼저 시도해야 합니다.

DescribeDBClusters API 작업을 사용하여 인스턴스를 나열(Java 예제)

AWS SDK for Java, 특히 DescribeDBClusters API 작업을 사용하여 프로그래밍 방식으로 인스턴스 목록을 찾을 수 있습니다.

다음은 java 8에서 이와 같이 검색하는 방법에 대한 작은 예입니다.

AmazonRDS client = AmazonRDSClientBuilder.defaultClient(); DescribeDBClustersRequest request = new DescribeDBClustersRequest() .withDBClusterIdentifier(clusterName); DescribeDBClustersResult result = rdsClient.describeDBClusters(request); DBCluster singleClusterResult = result.getDBClusters().get(0); String pgJDBCEndpointStr = singleClusterResult.getDBClusterMembers().stream() .sorted(Comparator.comparing(DBClusterMember::getIsClusterWriter) .reversed()) // This puts the writer at the front of the list .map(m -> m.getDBInstanceIdentifier() + endpointPostfix + ":" + singleClusterResult.getPort())) .collect(Collectors.joining(","));

여기에서 pgJDBCEndpointStr은 다음과 같이 형식이 지정된 끝점 목록을 포함합니다.

my-node1.cksc6xlmwcyw.us-east-1-beta.rds.amazonaws.com:5432, my-node2.cksc6xlmwcyw.us-east-1-beta.rds.amazonaws.com:5432

변수 endpointPostfix는 애플리케이션이 설정하는 상수일 수 있습니다. 또는 클러스터의 단일 인스턴스에 대해 DescribeDBInstances API 작업을 쿼리하여 애플리케이션에서 이를 가져올 수 있습니다. 이 값은 AWS 리전 내에서 그리고 개별 고객에 대해 일정하게 유지됩니다. 따라서 API 호출을 저장하여 애플리케이션이 읽어 들이는 리소스 파일에서 이 상수를 유지합니다. 앞의 예에서는 다음과 같이 설정됩니다.

.cksc6xlmwcyw.us-east-1-beta.rds.amazonaws.com

API가 응답하지 않거나 응답하는 데 너무 오래 걸리는 경우 가용성을 위해 기본적으로 DB 클러스터의 Aurora 엔드포인트를 사용하는 것이 좋습니다. 엔드포인트는 DNS 레코드를 업데이트하는 데 걸리는 시간 내에 최신 상태가 됩니다. DNS 레코드를 엔드포인트로 업데이트하는 데 보통 30초도 걸리지 않습니다. 이는 애플리케이션이 사용하는 리소스 파일에 저장할 수 있습니다.

장애 조치 테스트

모든 경우에 두 개 이상의 DB 인스턴스를 포함한 DB 클러스터가 있어야 합니다.

서버 측에서 특정 API가 애플리케이션의 응답 방식을 테스트하는 데 사용할 수 있는 중단을 유발할 수 있습니다.

  • FailoverDBCluster - 이 작업은 DB 클러스터의 새 DB 인스턴스를 기록기로 승격하려고 시도합니다.

    다음 코드 예에서는 failoverDBCluster를 사용하여 중단을 일으키는 방법을 보여줍니다. Amazon RDS 클라이언트 설정에 대한 자세한 내용은 AWS SDK for Java 사용을 참조하세요.

    public void causeFailover() { final AmazonRDS rdsClient = AmazonRDSClientBuilder.defaultClient(); FailoverDBClusterRequest request = new FailoverDBClusterRequest(); request.setDBClusterIdentifier("cluster-identifier"); rdsClient.failoverDBCluster(request); }
  • RebootDBInstance – 이 API 작업에서는 장애 조치가 보장되지 않습니다. 하지만 라이터의 데이터베이스는 종료됩니다. 이를 사용하여 애플리케이션이 연결 끊기에 어떻게 반응하는지 테스트할 수 있습니다. ForceFailover 파라미터는 Aurora 엔진에 적용되지 않습니다. 대신 FailoverDBCluster API 작업을 사용합니다.

  • ModifyDBClusterPort 파라미터를 변경하면 클러스터의 노드가 새 포트에서 수신을 시작할 경우 중단을 유발합니다. 일반적으로 애플리케이션만 포트 변경을 제어하도록 하여 이 실패에 먼저 응답할 수 있습니다. 또한 종속된 엔드포인트를 적절하게 업데이트할 수 있는지 확인하세요. 이렇게 하려면 API 수준에서 수정한 사람이 포트를 수동으로 업데이트하도록 하면 됩니다. 또는 애플리케이션의 RDS API를 사용하여 포트가 변경되었는지 확인할 수 있습니다.

  • ModifyDBInstance - DBInstanceClass 파라미터를 수정하면 중단이 발생합니다.

  • DeleteDBInstance – 기본(라이터)을 삭제하면 새 DB 인스턴스가 DB 클러스터의 라이터로 승격됩니다.

애플리케이션 또는 클라이언트 측에서 Linux를 사용하는 경우 애플리케이션이 갑작스러운 패킷 드롭에 응답하는 방식을 테스트할 수 있습니다. 포트, 호스트 또는 iptables 명령을 사용하여 TCP keepalive 패킷을 보내거나 받는지 여부에 따라 이 작업을 수행할 수 있습니다.

Java의 빠른 장애 조치 예

다음 코드 예는 애플리케이션이 Aurora PostgreSQL 드라이버 관리자를 설정하는 방식을 보여줍니다.

애플리케이션이 연결해야 할 때 getConnection 함수를 호출합니다. getConnection 호출은 유효한 호스트를 찾지 못할 수 있습니다. 라이터를 찾을 수 없지만 targetServerType 파라미터가 primary로 설정된 경우를 예로 들 수 있습니다. 이 경우 호출 애플리케이션에서 함수 호출을 다시 시도해야 합니다.

재시도 동작을 애플리케이션에 푸시하지 않으려면 이 재시도 호출을 연결 풀러로 래핑할 수 있습니다. 대부분의 연결 풀러에서는 JDBC 연결 문자열을 지정할 수 있습니다. 따라서 애플리케이션은 getJdbcConnectionString을 호출하고 이를 연결 풀러에 전달할 수 있습니다. 이렇게 하면 Aurora PostgreSQL에서 더 빠른 장애 조치를 사용할 수 있습니다.

import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; import java.sql.Statement; import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; import java.util.stream.IntStream; import org.joda.time.Duration; public class FastFailoverDriverManager { private static Duration LOGIN_TIMEOUT = Duration.standardSeconds(2); private static Duration CONNECT_TIMEOUT = Duration.standardSeconds(2); private static Duration CANCEL_SIGNAL_TIMEOUT = Duration.standardSeconds(1); private static Duration DEFAULT_SOCKET_TIMEOUT = Duration.standardSeconds(5); public FastFailoverDriverManager() { try { Class.forName("org.postgresql.Driver"); } catch (ClassNotFoundException e) { e.printStackTrace(); } /* * RO endpoint has a TTL of 1s, we should honor that here. Setting this aggressively makes sure that when * the PG JDBC driver creates a new connection, it will resolve a new different RO endpoint on subsequent attempts * (assuming there is > 1 read node in your cluster) */ 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"); } public Connection getConnection(String targetServerType) throws SQLException { return getConnection(targetServerType, DEFAULT_SOCKET_TIMEOUT); } public Connection getConnection(String targetServerType, Duration queryTimeout) throws SQLException { Connection conn = DriverManager.getConnection(getJdbcConnectionString(targetServerType, queryTimeout)); /* * A good practice is to set socket and statement timeout to be the same thing since both * the client AND server will stop the query at the same time, leaving no running queries * on the backend */ Statement st = conn.createStatement(); st.execute("set statement_timeout to " + queryTimeout.getMillis()); st.close(); return conn; } private static String urlFormat = "jdbc:postgresql://%s" + "/postgres" + "?user=%s" + "&password=%s" + "&loginTimeout=%d" + "&connectTimeout=%d" + "&cancelSignalTimeout=%d" + "&socketTimeout=%d" + "&targetServerType=%s" + "&tcpKeepAlive=true" + "&ssl=true" + "&loadBalanceHosts=true"; public String getJdbcConnectionString(String targetServerType, Duration queryTimeout) { return String.format(urlFormat, getFormattedEndpointList(getLocalEndpointList()), CredentialManager.getUsername(), CredentialManager.getPassword(), LOGIN_TIMEOUT.getStandardSeconds(), CONNECT_TIMEOUT.getStandardSeconds(), CANCEL_SIGNAL_TIMEOUT.getStandardSeconds(), queryTimeout.getStandardSeconds(), targetServerType ); } private List<String> getLocalEndpointList() { /* * As mentioned in the best practices doc, a good idea is to read a local resource file and parse the cluster endpoints. * For illustration purposes, the endpoint list is hardcoded here */ List<String> newEndpointList = new ArrayList<>(); newEndpointList.add("myauroracluster.cluster-c9bfei4hjlrd.us-east-1-beta.rds.amazonaws.com:5432"); newEndpointList.add("myauroracluster.cluster-ro-c9bfei4hjlrd.us-east-1-beta.rds.amazonaws.com:5432"); return newEndpointList; } private static String getFormattedEndpointList(List<String> endpoints) { return IntStream.range(0, endpoints.size()) .mapToObj(i -> endpoints.get(i).toString()) .collect(Collectors.joining(",")); } }