회원 등급별 거래 수수료율을 내리는 작업이 들어왔는데 fee_policies 에 새 정책을 만들어 활성화하면 끝날 것으로 보였다. 실행하기 전에 기존 회원 등급이 새 정책으로 옮겨지는지를 FeePolicyService 에서 확인했다.
Table of contents
Open Table of contents
배경 — 활성화 하나로 보이던 작업
activate() 를 부르면 정책이 ACTIVE 가 되고 체결 엔진의 수수료 캐시가 갱신된다. 여기까지는 활성화 과정에서 자동으로 처리되는 부분이다.
문제는 이미 있는 member_grades 가 fee_policy_id 로 어느 정책을 가리키느냐였다. 그 연결이 안 바뀌면 수수료율도 옛 값 그대로 나간다.
원인 — 버전 분기가 가른 이관
이관 코드를 열어 보니 policy.getVersion() == 1 일 때만 기존 등급을 새 정책으로 옮기고 있었다. 이번 정책은 maker·taker 를 나눈 v2 였으므로 그 조건에 해당하지 않았다.
기존 정책을 INACTIVE 로 바꾸는 코드도 그와 같은 분기 안에 들어 있었다. 결국 v2 정책은 이관도 해제도 일어나지 않는 상태였다.
중복 활성으로 공존하는 상태
그러면 새 정책과 기존 정책이 둘 다 ACTIVE 로 남고 등급은 기존 정책을 가리킨다. 수수료율을 바꾸려던 회원 수만 명이 그대로 옛 정책의 taker_rate 로 체결된다.
작업의 목적은 수수료 인하인데 그 목적 자체가 달성되지 않는 상태였다. 새 정책이 생겼다는 사실과 수수료가 바뀌었다는 사실이 별개였다.
주의 — 성공처럼 보이는 세 가지
activate() 완료 메시지가 뜨고 체결 엔진 캐시에 새 정책이 실리고 관리 화면 fee_policies 목록에 새 정책이 보인다. 이 셋이 나란히 보이면 작업이 성공한 것으로 읽힌다.
그래서 활성화했으니 끝났다고 판단하고 넘어가기 쉬운 자리였다. 실제로 확인해야 하는 것은 그 셋이 아니라 member_grades.fee_policy_id 가 어느 정책을 가리키는지 하나였다.
판단 기준 — 자동으로 안 되는 것과 하면 안 되는 것은 다르다
완전한 정리에는 신규 정책 활성화와 기존 정책 비활성화와 등급 재매핑 세 단계가 필요했고 자동인 것은 첫째뿐이었다. 두 가지 방법이 있었다. 분기를 고쳐 v2 도 자동 이관하게 하는 것과, 나머지 둘을 사람이 별도로 수행하는 절차로 문서에 적는 것이다. 뒤쪽을 골랐다. 이유는 v2 정책이 체결 엔진 캐시와 묶여 있어 일괄 재매핑이 체결 도중의 수수료를 바꿀 수 있어서다. 대신 정책을 바꿀 때마다 사람 손이 두 번 가는 것을 감수했다.
이 분기가 왜 있는지 생각해 보니 같은 이유로 일부러 뺀 것일 수 있었다. 그렇다면 결함이 아니라 수동 절차가 정답인 자리이므로 자동으로 안 된다와 자동으로 하면 안 된다를 구분해서 적어야 했다.
정리
- 자동 이관 로직에
version분기가 있을 수 있다 - 해당하지 않으면 이관도 해제도 안 된다
- 그러면 신규와 기존이
ACTIVE로 공존한다 - 대상은 옛것에 물린 채로 남는다
- 성공 메시지와 새 레코드와 캐시 반영이 다 보여 끝난 줄 안다
- 확인할 것은
member_grades.fee_policy_id가 바뀌었는지 하나다 - 완전 정리에 필요한 단계를 세어 별도로 수행한다
- 자동으로 안 되는 것과 하면 안 되는 것을 구분해 적는다