jeongmyeong zZZ,,

Maria DB(MySQL) Connector Troubleshooting

MariaDB Connector?

MariaDB connector 는 Type4 JDBC 드라이버입니다.

MariaDB 와 MySQL 데이터베이스 서버와 함께 사용하기 위한 경량 JDBC 커넥터


겪었던 문제

아래가 문제의 코드인데, db 에 데이터를 반영하고 난 뒤 재 조회를 하게 되는 경우 업데이트 되지 않는채로 데이터가 조회 되는 일이 발생했다.

가끔씩은 반영 된 데이터가 조회되기도 한다.

function run () {
    // 조회
    var order = orderService.find()
    
    // 수정
    orderService.updateSomething()
    
    // 재조회
    var updatedOrder = orderService.find()
    
    // 업데이트 되지 않은 정보가 조회된다 ??????
}

class OrderService {
    @Transactional(readOnly = true)
    fun find() {
        // 주문 조회
    }
    
    fun updateSomething() {
        // 수정
    }
}

이 문제를 겪기 전과 후에 변경 된 점은 OrderService 에 있는 @Transactional(readOnly = true) 구문이다.

먼저 JDBC connection 을 확인해보자

우선 JDBC connection 을 property 로 정의하는 방식은 아래와 같다.

jdbc:(mysql mariadb):[replication: loadbalance: sequential: aurora:]//[,...]/[database][?=[&=]]

1.5.1 버전 이전에는 jdbc:mysql:aurora://클러스터엔드포인트:포트,리더엔드포인트:포트 이런식으로도 사용했으나, 그 뒤로는 클러스터엔드포인트만 명시하는 것을 권고

ha mode

이 중 replication, loadbalance, sequential, aurora 등의 옵션을 maria db connector 에선 HA mode (High availability) 라고 정의하고 있다.

HA Mode 에 따라 Failover 기능을 지원하는데, Aurora 는 Master 의 상태를 보고 Slave 중 하나를 Master 로 승격시키는 방식으로 동작한다. (그 사이에 Scale 조정 및 tier 확인등을 통한 우선순위 산정 등의 과정이 포함되지만, 생략한다.)

connection

문제가 발생하던 시점엔 서비스에서 ha mode 를 aurora 로 준 상태로 실행되고 있었다.


Aurora Listener

이 경우 AuroraListener 에서 cluster endpoint 에 대한 연결 된 노드들의 endpoint 를 protocol query 로 실행해 가져오는 것을 볼 수 있다.

현재 문제가 재현되는 환경에선 master 한대와 Read replica 한대가 조회 되었다.

이는 실제 해당 데이터베이스에 쿼리를 직접 실행해도 확인할 수 있다.

Protocol Query



그래서 ReadOnly 가 왜?

Spring 에서 Transactional 을 처리하는 과정에 Transactional Annotation 에 대한 definition 을 파헤치는 부분이 있다.

DatasourceUtil

위 코드와 같이 DataSourceUtil 에서 readonly 의 여부에 따라 connection 을 readOnly 속성으로 설정해주는 곳이 있다.

MasterSlavesListener

이 경우 실제로 MasterSlavesListener 에서 connection 을 아까 Aurora protocol query 를 통해 받아온 read replica (slave) 의 connection 으로 전환 하는 것을 볼 수 있다.


DB Aurora cluster 주소와, aurora HA mode 옵션만으로 readOnly 프로퍼티가 true 인 경우, read replica 로 쿼리가 발생하게 된다.

이 경우 update logic 을 통해 master 에 반영 된 데이터가 read replica 들로 동기화 되기 전에 데이터가 조회 될 수 있다는 뜻이다.


Failover 기능은 우리 서비스의 가용성을 위해 꼭 필요한 옵션이다.

오로라DB 에서 제공하는 failover 기능을 사용하기 위해 db connection 을 jdbc:mysql:aurora:{cluster endpoint} 와 같은 형태로 반영한 적이 있었는데, application 상에서 read replica 로 직접적인 쿼리가 발생할 수 있다는 점을 놓쳤다.

무거운 쿼리를 의도적으로 read replica 로 발생시킬 수는 있겠지만, 위와 같이 의도하지 않은 방향으로 서비스가 동작 할 수 있으니, 확인이 꼭 필요하다.

또한, 상위 트랜잭션이 있다면, 하위 트랜잭션이 기본적으로 상위 트랜잭션에 포함되기 때문에 이 문제를 마주쳐보지 못했을 수도 있다.

중첩 된 트랜잭션에서 하위 트랜잭션을 의도적으로 read replica 로 보내고 싶은 경우가 있다면 REQUIRES_NEW 와 같은 옵션으로 전파옵션을 명시하여야 한다.

참고 문서