𝗗𝟭 𝗥𝗲𝗮𝗱 𝗥𝗲𝗽𝗹𝗶𝗰𝗮𝘀 𝗛𝗮𝗱 𝟲 𝗦𝗲𝗰𝗼𝗻𝗱𝘀 𝗼𝗳 𝗟𝗮𝗴
도쿄에 있는 D1 읽기 복제본(read replica)이 북미에서 발생한 쓰기 작업보다 6.1초 뒤처졌습니다.
잘못된 노출(impression)을 제한하는 트래커를 통해 이 사실을 알게 되었습니다. 문서는 최종 일관성(eventual consistency)을 언급하지만, 계획을 세울 수 있는 구체적인 시간은 명시하지 않습니다.
실제 수치를 확인하기 위해 데이터 신선도(staleness) 프로브를 구축했습니다. 이 프로브는 UUID와 epoch를 포함한 행을 작성합니다. 그 후 해당 행이 나타날 때까지 복제본을 폴링(poll)하며, 지연 시간을 기록합니다.
아시아 지역에서 200번의 프로브를 실행한 결과:
- p50: 800ms
- p95: 3,400ms
- p99: 6,100ms
프라이머리(primary)가 북미에 있고 사용자가 아시아에 있다면 지연 시간이 매우 높습니다.
스키마 오류도 겪었습니다. 프라이머리에서 마이그레이션이 실행된 후 Worker가 재시작되었습니다. 그런데 첫 번째 요청들이 새로운 테이블이 반영되기 전에 복제본에 도달했습니다. 오류 메시지에는 테이블이 존재하지 않는다고 나왔습니다. 테이블은 분명히 있었지만, 복제본이 뒤처져 있었던 것입니다.
저는 지연과 싸우는 대신, 지연을 우회하는 방식으로 이 문제를 해결했습니다.
설계 방식은 다음과 같습니다:
- 작성자(writer)는 행에
written_atepoch를 추가합니다. - 작성자는 응답에
X-D1-Written-At헤더를 추가합니다. - 읽기 작업(reader)은 해당 헤더를 복제본의 데이터와 비교합니다.
- 복제본 데이터가 헤더보다 오래되었다면, 읽기 작업은 KV로 폴백(fallback)합니다.
KV는 동일 지역 내에서 500ms 미만으로 실행됩니다. 하루 최대 1,000만 건의 읽기까지 무료입니다. 이는 중요한 플래그(flag)에 대해 최신 데이터를 가져올 수 있는 저렴한 방법입니다.
복제본이 뒤처져 있는 짧은 시간 동안에만 KV를 사용하게 됩니다. 복제본이 동기화되면 대부분의 읽기 요청은 정상적으로 D1을 통해 처리됩니다.
상세 포스트에서 전체 스크립트와 마이그레이션 패턴을 공유했습니다.