Failover cepat dengan Amazon Aurora PostgreSQL - Amazon Aurora

Terjemahan disediakan oleh mesin penerjemah. Jika konten terjemahan yang diberikan bertentangan dengan versi bahasa Inggris aslinya, utamakan versi bahasa Inggris.

Failover cepat dengan Amazon Aurora PostgreSQL

Di bagian berikut ini, Anda dapat mempelajari cara memastikan failover terjadi secepat mungkin. Untuk memulihkan dengan cepat setelah failover, Anda dapat menggunakan manajemen cache klaster untuk klaster DB Aurora PostgreSQL Anda. Untuk informasi selengkapnya, lihat Pemulihan cepat setelah failover dengan manajemen cache klaster untuk Aurora PostgreSQL.

Beberapa langkah yang dapat Anda ambil untuk membuat failover beperforma cepat antara lain sebagai berikut:

  • Atur keepalive Protokol Kontrol Transmisi (TCP) dengan rentang waktu singkat, untuk menghentikan kueri yang berjalan lebih lama sebelum batas waktu baca berakhir jika ada kegagalan.

  • Atur batas waktu untuk caching Java Sistem Nama Domain (DNS) secara agresif. Tindakan ini akan membantu memastikan titik akhir hanya baca Aurora dapat disikluskan dengan benar melalui simpul hanya baca pada percobaan koneksi selanjutnya.

  • Atur variabel batas waktu yang digunakan dalam string koneksi JDBC serendah mungkin. Gunakan objek koneksi terpisah untuk kueri berjalan singkat dan berjalan lama.

  • Gunakan titik akhir baca dan tulis Aurora yang diberikan untuk terhubung ke klaster.

  • Gunakan operasi API RDS untuk menguji respons aplikasi saat terjadi kegagalan sisi server. Selain itu, gunakan alat penghapusan paket untuk menguji respons aplikasi terhadap kegagalan sisi klien.

  • Gunakan Driver AWS JDBC untuk memanfaatkan sepenuhnya kemampuan failover Aurora PostgreSQL. Untuk informasi selengkapnya tentang Driver AWS JDBC dan petunjuk lengkap untuk menggunakannya, lihat repositori Amazon Web Services (AWS) JDBC Driver. GitHub

Hal ini dibahas secara lebih terperinci sebagai berikut.

Mengatur parameter keepalive TCP

Saat Anda mengatur koneksi TCP, serangkaian pengatur waktu dikaitkan dengan koneksi. Saat pengatur waktu keepalive mencapai nol, paket probe keepalive dikirim ke titik akhir koneksi. Jika probe menerima balasan, Anda dapat mengasumsikan bahwa koneksi masih aktif dan berjalan.

Dengan mengaktifkan parameter keepalive TCP dan mengaturnya secara agresif, akan dipastikan bahwa jika klien Anda tidak dapat terhubung ke basis data, koneksi aktif apa pun akan ditutup dengan cepat. Aplikasi kemudian dapat terhubung ke titik akhir baru.

Pastikan untuk mengatur parameter keepalive TCP berikut:

  • tcp_keepalive_time mengontrol waktu, dalam hitungan detik, setelah paket keepalive dikirim ketika tidak ada data yang dikirim oleh soket. ACK tidak dianggap sebagai data. Kami menyarankan pengaturan berikut:

    tcp_keepalive_time = 1

  • tcp_keepalive_intvl mengontrol waktu, dalam hitungan detik, di antara waktu pengiriman paket keepalive berikutnya setelah paket awal dikirim. Atur waktu ini dengan menggunakan parameter tcp_keepalive_time. Kami menyarankan pengaturan berikut:

    tcp_keepalive_intvl = 1

  • tcp_keepalive_probes adalah jumlah probe keepalive yang tidak diakui yang terjadi sebelum aplikasi diberi tahu. Kami menyarankan pengaturan berikut:

    tcp_keepalive_probes = 5

Pengaturan ini harus memberi tahu aplikasi dalam waktu lima detik ketika basis data berhenti merespons. Jika paket keepalive sering dihapus di dalam jaringan aplikasi, Anda dapat mengatur nilai tcp_keepalive_probes yang lebih tinggi. Tindakan ini memungkinkan lebih banyak buffer di jaringan yang kurang andal, meskipun akan menambah waktu yang dibutuhkan untuk mendeteksi kegagalan sebenarnya.

Untuk mengatur parameter keepalive TCP di Linux
  1. Uji konfigurasi parameter TCP keepalive Anda.

    Untuk melakukannya, kami merekomendasikan penggunaan baris perintah dengan perintah berikut. Konfigurasi yang disarankan ini ditujukan untuk seluruh sistem. Dengan kata lain, konfigurasi ini juga memengaruhi semua aplikasi lain yang membuat soket dengan opsi SO_KEEPALIVE aktif.

    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. Setelah Anda menemukan konfigurasi yang cocok untuk aplikasi Anda, pertahankan pengaturan ini dengan menambahkan baris berikut ke /etc/sysctl.conf, termasuk setiap perubahan yang Anda buat:

    tcp_keepalive_time = 1 tcp_keepalive_intvl = 1 tcp_keepalive_probes = 5

Mengonfigurasi aplikasi Anda untuk failover cepat

Di bagian berikut ini, Anda dapat menemukan diskusi tentang beberapa perubahan konfigurasi untuk Aurora PostgreSQL yang dapat Anda buat untuk failover cepat. Untuk mempelajari selengkapnya tentang penyiapan dan konfigurasi driver JDBC PostgreSQL, lihat dokumentasi Driver JDBC PostgreSQL.

Mengurangi batas waktu cache DNS

Saat aplikasi Anda mencoba membuat koneksi setelah failover, penulis Aurora PostgreSQL baru akan menjadi pembaca sebelumnya. Anda dapat menemukannya dengan menggunakan titik akhir hanya baca Aurora sebelum pembaruan DNS disebarkan sepenuhnya. Mengatur time to live (TTL) DNS java ke nilai rendah, seperti kurang dari 30 detik, akan membantu siklus di antara simpul pembaca pada percobaan koneksi selanjutnya.

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

Mengatur string koneksi Aurora PostgreSQL untuk failover cepat

Untuk menggunakan failover cepat Aurora PostgreSQL, pastikan bahwa string koneksi aplikasi Anda memiliki daftar host, bukan hanya satu host. Berikut ini adalah contoh string koneksi yang dapat Anda gunakan untuk terhubung ke klaster Aurora PostgreSQL. Dalam contoh ini, host dicetak tebal.

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

Untuk ketersediaan terbaik dan untuk menghindari ketergantungan pada API RDS, kami sarankan Anda mempertahankan file yang digunakan untuk terhubung. File ini berisi string host yang dibaca aplikasi sejak Anda membuat koneksi ke basis data. String host ini memiliki semua titik akhir Aurora yang tersedia untuk klaster. Untuk informasi selengkapnya tentang titik akhir Aurora, lihat Koneksi titik akhir Amazon Aurora.

Misalnya, Anda dapat menyimpan titik akhir Anda dalam file lokal seperti yang ditunjukkan berikut ini.

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

Aplikasi Anda akan membaca dari file ini untuk mengisi bagian host dalam string koneksi JDBC. Mengganti nama klaster DB akan menyebabkan perubahan pada titik akhir ini. Pastikan bahwa aplikasi Anda menangani peristiwa ini jika terjadi.

Opsi lainnya adalah menggunakan daftar simpul instans DB sebagai berikut.

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

Kelebihan dari pendekatan ini adalah driver koneksi JDBC PostgreSQL akan melewati semua simpul pada daftar ini untuk menemukan koneksi yang valid. Sebaliknya, ketika Anda menggunakan titik akhir Aurora, hanya dua simpul yang akan dicoba pada setiap percobaan koneksi. Namun, ada kekurangan pada penggunaan simpul instans DB. Jika Anda menambahkan atau menghapus simpul dari klaster dan daftar instans titik akhir menjadi kedaluwarsa, driver koneksi mungkin tidak akan menemukan host yang tepat untuk terhubung.

Untuk membantu memastikan bahwa aplikasi Anda tidak menunggu terlalu lama untuk terhubung ke satu host, atur parameter berikut secara agresif:

  • targetServerType – Mengontrol apakah driver terhubung ke simpul tulis atau simpul baca. Untuk memastikan bahwa aplikasi Anda hanya terhubung kembali ke simpul tulis, atur nilai targetServerType ke primary.

    Nilai untuk parameter targetServerType mencakup primary, secondary, any, dan preferSecondary. Nilai preferSecondary akan mencoba membuat koneksi ke pembaca terlebih dahulu. Koneksi dibuat ke penulis jika tidak ada koneksi pembaca yang dapat dibuat.

  • loginTimeout – Mengontrol waktu tunggu aplikasi untuk login ke basis data setelah koneksi soket dibuat.

  • connectTimeout – Mengontrol waktu tunggu soket untuk membuat koneksi ke basis data.

Anda dapat mengubah parameter aplikasi lain untuk mempercepat proses koneksi, tergantung pada seberapa agresif perilaku aplikasi yang Anda inginkan.

  • cancelSignalTimeout – Di beberapa aplikasi, Anda mungkin ingin mengirim sinyal pembatalan “percobaan terbaik” pada kueri yang telah habis batas waktunya. Jika sinyal pembatalan ini berada di jalur failover Anda, pertimbangkan untuk mengaturnya secara agresif agar sinyal ini tidak dikirim ke host yang nonaktif.

  • socketTimeout – Parameter ini mengontrol waktu tunggu soket untuk operasi baca. Parameter ini dapat digunakan sebagai "batas waktu kueri" global untuk memastikan tidak ada kueri yang menunggu lebih lama dari nilai ini. Praktik yang baik adalah memiliki dua handler koneksi. Satu handler koneksi menjalankan kueri yang bermasa aktif singkat dan menetapkan nilai ini lebih rendah. Handler koneksi lain, untuk kueri yang berjalan lama, mengatur nilai ini jauh lebih tinggi. Dengan pendekatan ini, Anda dapat mengandalkan parameter keepalive TCP untuk menghentikan kueri yang berjalan lama jika server mengalami gangguan.

  • tcpKeepAlive – Aktifkan parameter ini untuk memastikan parameter keepalive TCP yang Anda tetapkan dipatuhi.

  • loadBalanceHosts – Saat ditetapkan ke true, parameter ini akan membuat aplikasi terhubung ke host acak yang dipilih dari daftar host kandidat.

Opsi lain untuk mendapatkan string host

Anda bisa mendapatkan string host dari beberapa sumber, termasuk fungsi aurora_replica_status dan dengan menggunakan API Amazon RDS.

Dalam banyak kasus, Anda perlu menentukan penulis klaster atau untuk menemukan simpul pembaca lainnya di klaster. Untuk melakukannya, aplikasi Anda dapat terhubung ke instans DB apa pun di klaster DB dan mengueri fungsi aurora_replica_status. Anda dapat menggunakan fungsi ini untuk mengurangi waktu yang dibutuhkan untuk menemukan host untuk terhubung. Namun, dalam skenario kegagalan jaringan tertentu aurora_replica_status fungsi mungkin menampilkan out-of-date atau informasi yang tidak lengkap.

Cara yang tepat untuk memastikan bahwa aplikasi Anda dapat menemukan simpul untuk terhubung adalah dengan mencoba terhubung ke titik akhir penulis klaster lalu titik akhir pembaca klaster. Anda melakukannya sampai Anda dapat membuat koneksi yang dapat dibaca. Titik akhir ini tidak berubah kecuali jika Anda mengganti nama klaster DB Anda. Dengan demikian, Anda umumnya dapat membiarkannya sebagai anggota statis aplikasi Anda atau menyimpannya dalam file sumber daya yang dibaca oleh aplikasi Anda.

Setelah membuat koneksi menggunakan salah satu titik akhir ini, Anda dapat memperoleh informasi tentang klaster lainnya. Untuk melakukannya, panggil fungsi aurora_replica_status. Misalnya, perintah berikut mengambil informasi dengan 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)

Misalnya, bagian host dalam string koneksi Anda mungkin dimulai dengan titik akhir klaster penulis dan pembaca, seperti yang ditunjukkan berikut.

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

Dalam skenario ini, aplikasi Anda mencoba membuat koneksi ke jenis simpul apa pun, primer atau sekunder. Saat aplikasi Anda terhubung, praktik yang baik adalah memeriksa terlebih dahulu status baca/tulis simpul tersebut. Untuk melakukannya, jalankan kueri untuk hasil perintah SHOW transaction_read_only.

Jika nilai yang dihasilkan dari kueri adalah OFF, berarti Anda berhasil terhubung ke simpul primer. Namun, misalkan nilai yang dihasilkan adalah ON dan aplikasi Anda memerlukan koneksi baca/tulis. Dalam hal ini, Anda dapat memanggil fungsi aurora_replica_status untuk menentukan server_id yang memiliki session_id='MASTER_SESSION_ID'. Fungsi ini akan memberikan nama simpul primer. Anda dapat menggunakannya dengan endpointPostfix yang dijelaskan berikut.

Pastikan Anda mengetahui saat Anda terhubung ke replika yang memiliki data yang kedaluwarsa (stale). Ketika ini terjadi, aurora_replica_status fungsi mungkin menampilkan out-of-date informasi. Anda dapat menetapkan ambang batas untuk staleness di tingkat aplikasi. Untuk memeriksanya, Anda dapat melihat perbedaan antara waktu server dan nilai last_update_timestamp. Secara umum, aplikasi Anda harus menghindari peralihan di antara dua host karena informasi yang bertentangan yang dihasilkan oleh fungsi aurora_replica_status. Aplikasi Anda harus mencoba semua host yang diketahui terlebih dahulu, bukan mengikuti data yang dihasilkan oleh aurora_replica_status.

Menampilkan daftar instans menggunakan operasi API DescribeDBClusters beserta contohnya di Java

Anda dapat menemukan daftar instans secara programatis menggunakan AWS SDK for Java, khususnya operasi API DescribeDBClusters.

Berikut ini adalah contoh sederhana tentang cara melakukannya di 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(","));

Di sini, pgJDBCEndpointStr berisi daftar titik akhir berformat, seperti yang ditunjukkan berikut ini.

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

Variabel endpointPostfix dapat berupa konstanta yang ditetapkan aplikasi Anda. Atau, aplikasi Anda bisa mendapatkannya dengan mengueri operasi API DescribeDBInstances untuk satu instans di klaster Anda. Nilai ini tetap konstan dalam Wilayah AWS dan untuk pelanggan individu. Jadi, variabel tersebut menyimpan panggilan API hanya untuk mempertahankan konstanta ini dalam file sumber daya yang dibaca aplikasi Anda. Dalam contoh sebelumnya, variabel tersebut diatur sebagai berikut.

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

Untuk tujuan ketersediaan, praktik yang baik adalah menggunakan titik akhir Aurora pada klaster DB Anda secara default jika API ini tidak merespons atau memerlukan waktu terlalu lama untuk merespons. Titik akhir dijamin akan diperbarui selama waktu yang diperlukan untuk memperbarui catatan DNS. Pembaruan catatan DNS dengan titik akhir biasanya membutuhkan waktu kurang dari 30 detik. Anda dapat menyimpan titik akhir dalam file sumber daya yang digunakan aplikasi Anda.

Menguji failover

Dalam semua kasus, Anda harus memiliki klaster DB yang berisi dua atau beberapa instans DB.

Dari sisi server, operasi API tertentu dapat menyebabkan pemadaman yang dapat digunakan untuk menguji respons aplikasi Anda:

  • FailoverDBCluster – Operasi ini mencoba mempromosikan instans DB baru di klaster DB Anda menjadi penulis.

    Contoh kode berikut menunjukkan cara Anda dapat menggunakan failoverDBCluster untuk menyebabkan pemadaman. Untuk detail selengkapnya tentang menyiapkan klien Amazon RDS, lihat Menggunakan AWS SDK for Java.

    public void causeFailover() { final AmazonRDS rdsClient = AmazonRDSClientBuilder.defaultClient(); FailoverDBClusterRequest request = new FailoverDBClusterRequest(); request.setDBClusterIdentifier("cluster-identifier"); rdsClient.failoverDBCluster(request); }
  • RebootDBInstance – Failover tidak dijamin dengan operasi API ini. Namun, operasi ini menonaktifkan basis data pada penulis. Anda dapat menggunakannya untuk menguji respons aplikasi Anda terhadap koneksi yang terputus. Parameter ForceFailover tidak berlaku untuk mesin Aurora. Sebagai gantinya, gunakan operasi API FailoverDBCluster.

  • ModifyDBCluster – Mengubah parameter Port akan menyebabkan pemadaman saat simpul dalam klaster mulai mendengarkan di port baru. Secara umum, aplikasi Anda dapat merespons kegagalan ini terlebih dahulu dengan memastikan bahwa hanya aplikasi Anda yang mengontrol perubahan port. Selan itu, pastikan bahwa aplikasi Anda dapat dengan tepat memperbarui titik akhir yang diandalkannya. Anda dapat melakukannya dengan meminta seseorang memperbarui port secara manual saat melakukan perubahan di tingkat API. Atau, Anda dapat melakukannya dengan menggunakan API RDS di aplikasi Anda untuk menentukan apakah port telah berubah.

  • ModifyDBInstance – Mengubah parameter DBInstanceClass akan menyebabkan pemadaman.

  • DeleteDBInstance – Menghapus instans DB primer (penulis) akan menyebabkan instans DB baru dipromosikan menjadi penulis di klaster DB Anda.

Dari sisi aplikasi atau klien, jika Anda menggunakan Linux, Anda dapat menguji respons aplikasi terhadap penghapusan paket yang tiba-tiba. Anda dapat melakukannya berdasarkan apakah port atau host telah berubah, atau apakah paket keepalive TCP dikirim atau diterima dengan menggunakan perintah iptables.

Contoh failover cepat di Java

Contoh kode berikut menunjukkan cara aplikasi dapat mengatur pengelola driver Aurora PostgreSQL.

Aplikasi memanggil fungsi getConnection saat memerlukan koneksi. Panggilan ke getConnection dapat gagal menemukan host yang valid. Contohnya adalah ketika tidak ada penulis yang ditemukan, tetapi parameter targetServerType diatur ke primary. Dalam hal ini, aplikasi yang memanggil harus mencoba memanggil lagi fungsi tersebut.

Untuk menghindari mendorong perilaku coba lagi ke aplikasi, Anda dapat menggabungkan panggilan coba lagi ini ke pooler koneksi. Dengan sebagian besar pooler koneksi, Anda dapat menentukan string koneksi JDBC. Jadi, aplikasi Anda dapat memanggil getJdbcConnectionString dan meneruskannya ke pooler koneksi. Tindakan ini memungkinkan Anda menggunakan failover lebih cepat dengan 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(",")); } }