Skip to content
isdnetworks
Go back

방금 넣은 걸 못 읽는다

레코드를 만들고 바로 그 값을 조회해 반환하는 코드가 간헐적으로 실패했다. 방금 넣은 것을 못 찾는다는 오류였다.

Table of contents

Open Table of contents

읽기와 쓰기가 갈린 경로

앞단에 라우터가 있어서 쿼리를 종류에 따라 나눠 보내고 있었다. 삽입은 주 서버로 가고 조회는 복제본으로 간다.

복제에는 몇 밀리초의 지연이 있고 그 사이에 조회하면 아직 없는 상태다. 값이 없는 것이 아니라 아직 도착하지 않은 것이었다.

트랜잭션이면 주 서버라는 말의 조건

명시적 트랜잭션 안에 넣으면 같은 서버로 간다는 말을 들었지만 조건이 붙어 있었다. 라우터 설정을 직접 열어 보니 낙관적 트랜잭션 옵션이 꺼져 있어서 평문 트랜잭션은 주 서버로 간다.

다만 읽기 전용을 명시한 트랜잭션은 그 옵션과 무관하게 복제본으로 갈 수 있었다. 우리가 쓰는 라이브러리가 어느 쪽을 만드는지 확인하니 평문이었다.

같은 세션이 아니면 보장 밖

설정에는 쓰기 직후 읽기의 일관성을 세션 단위로 보장하는 항목도 있었다. 라우터가 복제 위치를 확인하고 기다려 주지만 그 범위가 같은 세션 안이다.

우리는 커넥션 풀을 쓰므로 삽입과 조회가 서로 다른 커넥션을 잡을 수 있다. 라우터 입장에서 다른 세션이 되고 보장이 사라지는 것이 간헐적 실패의 이유였다.

한 커넥션을 점유하는 트랜잭션

해결은 두 쿼리를 하나의 명시적 트랜잭션에 넣는 것이었다. 트랜잭션은 한 커넥션을 점유하므로 삽입과 조회가 같은 세션이 된다.

평문 트랜잭션이라 주 서버로 가고 같은 커넥션이라 일관성 보장도 유효하다. 두 조건이 동시에 충족되어야 이 패턴이 안전해진다.

전제가 우리 설정에서 참인지 확인했다

수정하기 전에 그 전제가 우리 설정에서 참인지를 따로 봤다. 들은 말이 일반적으로 맞아도 이 환경에서 맞는지는 별개이기 때문이다.

라우터 설정 파일을 읽고 런타임에 덮어쓰는 경로가 있는지도 확인했다. 관리 인터페이스는 접근이 막혀 있어서 정적 설정과 덮어쓰기 부재 두 가지로 확인했고 하나만 봤으면 런타임에 바뀌었을 가능성이 남았을 것이다.

이 결론과 삽입 직후 재조회 패턴은 명시적 트랜잭션으로 묶는다는 규칙을 문서에 적었다. 코드 리뷰 항목에도 넣었는데 목적은 그 패턴 자체를 알아보게 하는 것이었다.

정리


Share this post on:

Previous Post
초기값 상수가 라우팅을 정한다
Next Post
부수효과를 원하는지로 도구를 고른다