한 주문에 order_cancels 레코드가 둘 있었는데 cancel_type 과 quantity 와 amount 와 created_at 과 is_auto 까지 전부 같았다. 시스템이 중복으로 만든 것으로 보고 그중 하나를 지우려고 했다.
Table of contents
Open Table of contents
배경 — 라인 컬럼이 없는 테이블
어느 쪽을 지울지 정하려고 DESC order_cancels 로 구조를 열어 보니 order_item_id 컬럼이 아예 없었다. 그 테이블만 보면 취소가 주문 단위 개념인 것처럼 보이는 스키마였다.
관련 테이블을 더 찾아보니 취소와 라인을 잇는 order_cancel_items 피벗 테이블이 따로 존재했다. 그것을 JOIN 해 조회하니 두 취소가 각각 서로 다른 order_item_id 를 가리키고 있었다.
원인 — 값이 같았던 이유
중복이 아니라 라인별로 하나씩 만들어진 취소였고 왜 값이 전부 같았는지도 설명이 됐는데 두 라인의 unit_price 가 같았기 때문이다.
같은 상품의 다른 옵션이거나 가격이 같은 다른 상품이면 취소 금액과 수량이 우연히 일치한다. 같은 요청으로 동시에 생성됐으니 생성 시각까지 같았다. 하나를 지웠으면 그 라인은 취소가 안 된 채로 남아 정산에 그대로 잡혔을 것이다.
조치 — 매핑으로 하는 판정
판정 방식은 두 가지가 있었는데 값 다섯 개가 같으면 중복으로 보는 것과 반드시 order_cancel_items 매핑으로 확정하는 것이다. 뒤쪽을 골랐다. 이유는 값이 아무리 같더라도 매핑이 다르면 서로 다른 건이라서다. 대신 중복 점검 쿼리마다 조인이 하나 더 붙어 느려지는 것을 감수했다.
같은 확인을 다른 건에도 적용해 보니 앞선 판단 하나가 그대로 정정됐다. 정체된 취소가 구매확정 라인 때문이라고 봤는데 매핑을 보니 서로 다른 라인이라 무관했고 구매확정 철회가 필요 없었다.
검증 — 안 해도 되는 작업을 피한 것
구매확정 철회는 결코 가벼운 작업이 아닌데 정산에 영향이 가고 고객에게 상태 변경이 통지되며 되돌리기도 어렵다.
매핑 조회 하나가 안 해도 되는 그 작업을 하지 않게 막아 줬다. 값을 비교해 내린 판단을 매핑으로 다시 확인하는 절차가 그만큼의 값어치를 했다.
주의 — 스키마가 담은 역사
이 스키마가 왜 이런 모양이 됐는지도 어느 정도 짐작이 된다. 취소가 원래 주문 단위 개념이었다가 나중에 부분 취소가 생기면서 라인 매핑이 필요해졌을 것이다.
그때 피벗 테이블이 추가됐고 원래 테이블에는 라인 컬럼이 안 생겼다. 그래서 order_cancels 만 보면 여전히 주문 단위로 보인다. 스키마의 모양이 개념의 역사를 담고 있으므로 나중에 추가된 축은 별도 테이블에 있는지를 물어야 한다.
정리
- 값이 전부 같아도 중복이 아닐 수 있다
- 단가가 같으면 금액과 수량이 우연히 일치한다
- 동시 생성이면 생성 시각까지 같다
- 취소나 클레임은 피벗 테이블에 라인 매핑이 있을 수 있다
- 중복 판정은 값이 아니라 매핑으로 한다
- 매핑을 보면 앞선 판단이 정정되기도 한다
- 스키마의 모양이 개념의 역사를 담고 있다
- 이 테이블이 전부인지를 묻는다