레거시 전환에서 진짜 어려운 것은 데이터입니다
코드는 옮기면 되지만 데이터는 되돌리기 어렵습니다. 이중 쓰기 구간에서 반드시 확인해야 하는 것들.
스트랭글러 패턴은 잘 알려진 방법입니다. 라우팅 계층을 두고 기능을 하나씩 옮긴다. 문서로 보면 간단합니다. 그런데 실제 프로젝트에서 일정이 무너지는 지점은 거의 항상 데이터입니다.
코드는 되돌릴 수 있고 데이터는 어렵다
신규 시스템에서 버그가 발견되면 라우팅을 되돌리면 됩니다. 5분이면 끝납니다. 그런데 그 5분 동안 신규 시스템이 새 스키마에 쓴 데이터는 어떻게 되나요? 레거시는 그 데이터를 읽지 못합니다.
그래서 이행 순서는 항상 데이터가 먼저, 그리고 더 천천히입니다.
이중 쓰기 구간
이중 쓰기(dual write)는 같은 변경을 양쪽 저장소에 모두 쓰는 구간입니다. 여기서 확인해야 할 것들이 있습니다.
1. 쓰기 실패를 어떻게 처리하는가
레거시 쓰기는 성공하고 신규 쓰기가 실패하면 어떻게 되나요? 트랜잭션으로 묶을 수 없는 두 저장소라면 반드시 불일치가 생깁니다. 실무에서는 레거시를 진실의 원천으로 두고 신규 쓰기 실패는 재시도 큐로 보냅니다. 반대로 하면 되돌릴 수 없습니다.
2. 불일치를 어떻게 발견하는가
비교 배치를 매일 돌립니다. 전건 비교가 부담되면 최근 24시간 변경분만이라도 비교합니다. 불일치 건수가 0이 되지 않으면 읽기를 옮기지 않습니다. "거의 다 맞으니까 넘어가자"는 판단이 나중에 정산 오류로 돌아옵니다.
3. 자동 생성 값을 어떻게 맞추는가
자동 증가 ID, 생성 시각, 기본값은 양쪽에서 다르게 채워집니다. 신규 시스템이 값을 생성하지 말고 레거시가 만든 값을 그대로 받아쓰게 하는 것이 안전합니다.
읽기 전환
읽기는 한 번에 옮기지 않습니다. 신규에서 읽되 레거시와 비교해 다르면 레거시 결과를 반환하고 차이를 로그로 남기는 섀도 리드 구간을 둡니다. 이 구간에서 발견되는 차이의 대부분은 정렬 순서나 NULL 처리 같은 사소한 것이고, 사소한 차이가 실제 사고를 만듭니다.
레거시 쓰기 종료
마지막 단계입니다. 여기서부터는 되돌릴 수 없으므로, 종료 전에 최소 2주간 신규 시스템 단독 읽기를 운영해 보고 결정합니다. 종료 후에도 레거시는 읽기 전용으로 한 분기 유지합니다. 감사나 정산 문제로 과거 데이터를 확인해야 하는 일이 반드시 생깁니다.
일정 산정
경험상 데이터 이행 구간은 전체 일정의 40~50%를 차지합니다. 코드 이행 기준으로 일정을 짜면 반드시 늦습니다.