AWS JDBC Wrapper 란?
AWS에서 제공하는 AWS JDBC driver는 현재 deprecated되고 AWS wrapper로 대체되었다. AWS wrapper는 기존의 JDBC driver를 한 번 감싼 형태로, 편의성을 위해 여러 플러그인을 제공하는 DB connector이다.
이 중 DB 운영에 가장 유용하게 쓰일만한 플러그인은 단연 fast-failover 와 read/write splitting 플러그인이다.
- fast-failover: failover 발생 시 connection error 없이 빠른 failover가 가능
- read/write splitting: 하나의 접속스트링만으로도 reader/writer 전환 가능
https://github.com/aws/aws-advanced-jdbc-wrapper#about-the-driver
GitHub - aws/aws-advanced-jdbc-wrapper: The Amazon Web Services JDBC Driver has been redesigned as an advanced JDBC wrapper. Thi
The Amazon Web Services JDBC Driver has been redesigned as an advanced JDBC wrapper. This wrapper is complementary to and extends the functionality of an existing JDBC driver to help an application...
github.com
AWS JDBC Wrapper의 페일오버 동작 방식
어플리케이션에서 연결을 요청하면 C인스턴스(Primary)에 물리적으로 연결된 논리 커넥션을 반환하며, JDBC 드라이버를 사용하여 논리적 연결을 먼저 얻는다.
물리 접속처의 DB 인스턴스에 문제가 발생한 경우 아래 단계로 페일오버가 진행된다.
- 내부 토폴로지 캐시에서 활성 Aurora 복제본에 연결하며 새로운 Primary가 확인될때까지 계속 토폴로지 캐시를 쿼리한다.
- SQLState 08S02를 발생시켜 세션을 재구성하여 새 primary에 연결한다.
- Writer가 식별되면 물리적 대상을 전환한다.
테스트 1 - pgdriver & AWS wrapper 의 페일오버 성능을 비교해보자
오늘 포스팅에서는 Aurora Postgresql에 연결할 때 pgdriver vs AWS wrapper 에 따른 기능적/성능적 차이를 비교해보고자 한다.
테스트 환경
- OS: linux 4.14.243-185.433.amzn2.x86_64, arch: "amd64", family: "unix"
- Application
- JDBC Driver: AWS Advanced JDBC Driver 2.3.7 (2024.07 latest)
- HikariCP 5.0.1
- max connection pool size: 20
- connection timeout: 250ms(min)
- Java 17.0.11
- Apache Maven 3.9.4
- Spring CLI v3.1.0
- DB
- AWS Postgresql 14.6(LTS 버전) 1세트(1 writer/1 reader)
- t4g.medium(2 vCPU, 4GB mem)
테스트 시나리오
- pgDriver, AWS wrapper 둘 다 측정하여 페일오버 시간 및 에러 발생 여부를 비교한다.
- RW+RO 커넥션 스트링일 때와 RW 클러스터만 기재된 커넥션 스트링일 때로 나누어서 측정
- TTL=1s(min) 설정했을 때와 아닐 때(default 30s)
- db에는 master로만 SELECT 10 qps, INSERT 2 qps 발생
- AWS 콘솔에서 강제로 페일오버를 수행하고, db/어플리케이션 쪽에 발생하는 에러 및 exception을 확인한다.
SELECT: select now()
INSERT: INSERT INTO t1 (col1,col2) VALUES (?, now())
sr=> \d t1;
Table "public.t1"
Column | Type | Collation | Nullable | Default
--------+--------------------------+-----------+----------+---------
col1 | character varying(50) | | not null |
col2 | timestamp with time zone | | |
테스트 1 결과 & 결론
비교항목 / Driver 종류 | AWS Advanced JDBC (writer domain) |
AWS Advanced JDBC (writer+reader domain) |
pgJDBC (writer domain) |
pgJDBC (writer+reader domain) |
정상 failover 여부 | O | O | X | O |
failover 소요 시간(초) | 0.12 | 11 | 11 | 0.136 |
어플리케이션 에러 발생 여부 | FailoverSuccess SQLException 발생 |
O | O | O |
접속 에러 시간(초) | 8 | 7 | 7 | 7 |
- AWS wrapper(advanced JDBC driver)를 사용한 failover시에 어플리케이션 레벨에서는 에러가 발생하지 않고 jdbc 콜이 오래 걸린 것처럼 보인다. (논리적으로는 하나의 커넥션만 사용하면서 페일오버가 발생하면 내부적으로 물리 커넥션만 교체하는 방식을 사용)
- AWS wrapper가 가장 빠른 failover를 지원하며, 이 때 connection string은 writer 클러스터 도메인만 명시한다.
- pg JDBC driver를 사용하는 경우 RW+RO 클러스터 도메인을 모두 작성해야 failover가 지원된다.
- AWS wrapper 사용 시, 어플리케이션 로직에 페일오버 결과에 따른 exception 처리를 올바르게 해 주어야 에러가 발생하지 않는다.
AWS wrapper 사용 시 fast-failover를 위한 Failover Exception 처리
1. FailoverSuccessSQLException: 쿼리 실행이 실패하고 jdbc wrapper가 성공적으로 failover를 수행한 경우
→ 커넥션 재설정 및 쿼리 재시도 처리
2. FailoverFailedSQLException: 어플리케이션에서 커넥션을 새로 열어야 하는 경우
→ 트랜잭션이 실패했는지 확인하는 로직
3. TransactionStateUnknownSQLException: 트랜잭션 로직 실행 중 연결에 실패한 경우
→ wrapper는 성공적으로 새 writer에 연결된 상태이나 트랜잭션 상태를 알 수 없음
테스트 2 - AWS wrapper 의 R/W Splitting이 정말 잘 되는지 테스트해보자
테스트 시나리오
- RW 클러스터 커넥션 스트링으로만 연결하고, setReadOnly(true)를 통해 RO 클러스터로 커넥션 전환이 되는지 확인한다.
- 추가로 공식 문서의 내용 중 아래 사항을 확인한다.
- auto-commit, isolation level을 세션 레벨에서 설정하면 rw splitting 이후에도 유지되는지?
- 위의 두 파라미터를 제외한 다른 파라미터는 세션 레벨에서 설정 후 splitting이 발생하면 소실되는지?
- rw splitting시에 커넥션을 새로 생성해주어야 정상적으로 동작하는지?(기존 커넥션으로 넘어가면 에러 발생하는지)
테스트 결과 & 결론
세션 레벨에서 설정한 파라미터 | Writer 인스턴스 (splitting 전) |
Reader 인스턴스 (splitting 후) |
Writer 인스턴스(splitting 재시도) |
auto_commit | false | false | false |
transaction_isolation_level | repeatable read | repeatable read | repeatable read |
timezone(dynamic parameter) | Asia/Seoul | UTC(default) | Asia/Seoul |
- 한 커넥션 객체에서 r/w splitting을 해서 커넥션 전환을 한 경우, auto_commit과 트랜잭션 격리 수준은 유지되었다.
- 나머지 dynamic 파라미터의 경우 세션에서 설정한 값은 splitting 이후 사라지고 RO 인스턴스에서는 디폴트값으로 조회되었다.
- 다시 splitting을 해서 RW 인스턴스에서 조회하였을 때는 세션 파라미터 설정값이 유효하였다. 즉, 커넥션 객체를 반환하지 않은 상태로 splitting만 진행하는 경우 세션 레벨에서 설정한 dynamic 파라미터의 값은 인스턴스 안에서는 유지된다.
- Java 어플리케이션의 Connection 객체에서 setReadOnly(true) 가 최초로 호출되면 리더 인스턴스에 새로운 물리 커넥션을 생성하고, 해당 커넥션은 Connection 객체에 캐싱되어 같은 객체에서의 setReadOnly() 는 같은 커넥션을 반환하게 된다. 반면에 setReadOnly(true)를 새로운 Connection 객체에서 호출하는 경우에는 리더 인스턴스에 새 물리 커넥션을 생성하게 된다. wrapper는 클러스터의 각 인스턴스별로 커넥션 풀을 유지하며 이전 Connection 객체의 setReadOnly 호출로 생성된 커넥션을 재사용한다. 현재 wrapper는 내부 커넥션 풀링에 Hikari를 사용하고 있다. (참고)
테스트 2 결론
- setReadOnly(true) 를 사용하여 writer 클러스터 도메인만으로도 reader 접속이 가능하다.
- 커넥션 객체를 반환하기 전까지 writer, reader DB에 모두 커넥션이 계속 유지된다.
한 커넥션 객체에서 R/W splitting으로 커넥션을 전환한 경우, auto-commit 과 트랜잭션 격리 수준을 제외한 나머지 세션 레벨 파라미터의 설정값은 전환되지 않는다. - R/W splitting과 다른 플러그인을 함께 사용하려면 반드시 property 설정에서 readWriteSplitting을 선두에 명시해야 다른 플러그인이 제대로 동작한다. (현재 버전의 한계로 보임)
final Properties properties = new Properties();
properties.setProperty(PropertyDefinition.PLUGINS.name, "readWriteSplitting,failover,efm");
- 트랜잭션 중간에는 writer ↔︎ reader간 커넥션 전환이 불가능하다. 한 트랜잭션 안에서 splitting 시도 시 아래 에러가 발생한다.
Cannot change transaction read-only property in the middle of a transaction
결론
- AWS wrapper가 가장 빠른 failover를 지원하며, 이 때 connection string은 writer 클러스터 도메인만 명시한다.
- pg JDBC driver를 사용하는 경우 RW+RO 클러스터 도메인을 모두 작성해야 failover가 지원된다.
- AWS wrapper 사용 시, 어플리케이션 로직에 페일오버 결과에 따른 exception 처리를 올바르게 해 주어야 에러가 발생하지 않는다.
- setReadOnly(true) 를 사용하여 writer 클러스터 도메인만으로도 reader 접속이 가능하다. 그러나, 커넥션 객체를 반환하기 전까지 writer, reader DB에 모두 커넥션이 계속 유지된다.
- R/W splitting으로 커넥션을 전환한 경우 auto-commit 과 트랜잭션 격리 수준을 제외한 나머지 세션 레벨 파라미터의 설정값은 전환되지 않는다.
- R/W splitting과 다른 플러그인을 함께 사용하려면 반드시 property 설정에서 readWriteSplitting을 선두에 명시해야 다른 플러그인이 제대로 동작한다. (현재 버전의 한계로 보임)
참고
- Failover Configuration: https://github.com/aws/aws-advanced-jdbc-wrapper/blob/main/docs/using-the-jdbc-driver/FailoverConfigurationGuide.md#failover-configuration-guide
aws-advanced-jdbc-wrapper/docs/using-the-jdbc-driver/FailoverConfigurationGuide.md at main · aws/aws-advanced-jdbc-wrapper
The Amazon Web Services JDBC Driver has been redesigned as an advanced JDBC wrapper. This wrapper is complementary to and extends the functionality of an existing JDBC driver to help an application...
github.com
- Read-Write Splitting Configuration: https://github.com/aws/aws-advanced-jdbc-wrapper/blob/main/docs/using-the-jdbc-driver/using-plugins/UsingTheReadWriteSplittingPlugin.md#readwrite-splitting-plugin
aws-advanced-jdbc-wrapper/docs/using-the-jdbc-driver/using-plugins/UsingTheReadWriteSplittingPlugin.md at main · aws/aws-advanc
The Amazon Web Services JDBC Driver has been redesigned as an advanced JDBC wrapper. This wrapper is complementary to and extends the functionality of an existing JDBC driver to help an application...
github.com