지예환Ewan Jee
프로젝트 목록
서비스 A서비스 B잠금 A잠금 B같은 행 있나? 없음같은 옵션, 같은 상태같은 옵션, 같은 상태 (중복)

5 / 5. B도 행을 넣습니다. 같은 기록이 두 번 쌓였습니다.

무신사: Purchase 팀

정산 원장 중복 삽입

2026.09, 원인 분석, ADR 작성

  • 2천여 건중복 묶음(한 시점 표본)
  • 7개비교한 대안
  • 141가지가중치 조합 검증

정산 원장에 같은 기록이 두 번 쌓이는 원인을 두 서비스의 서로 다른 잠금과 커밋 전 이벤트로 좁히고, 대안 7개를 가중 결정표로 비교해 값 통일 뒤 원장 유니크 키를 제안했습니다. 가중치를 5%p씩 흔든 141가지 조합에서도 1위는 바뀌지 않았습니다.

문제

정산 원장은 주문 옵션이 출고 완료, 구매 확정, 환불 완료 같은 상태에 닿을 때마다 그때 금액을 한 행씩 쌓는 기록인데, (옵션, 상태) 조합에 유니크 키가 없어 같은 기록이 두 번 쌓일 수 있었습니다.

2026년 9월 14일 표본에서 중복 묶음 2천여 건을 찾았고, 출고 완료가 약 44%, 구매 확정이 약 35%, 환불 완료가 약 21%였습니다.

중복 한 쌍마다 그 옵션의 월 정산 합계가 2배로 부풀려졌습니다.

봇이 찾은 중복을 사람이 승인해 지워도 2시간이 안 돼 다시 생겼고, 그 뒤에도 매일 두 자릿수 규모로 재발했습니다.

2020년 1월에도 쿼리 수정으로 한 번 막았지만, 그 뒤 쓰기 경로가 늘며 다시 생겼습니다.

그래서 문제를 쓰기 경로 하나하나를 막는 일이 아니라, “한 옵션의 한 상태는 원장에 한 행만 있다”는 규칙을 원장이 지키게 하는 일로 정했습니다.

접근

기준과 가중치(지금 합계 100%, 점수는 합계를 100%로 맞춰 계산)
  1. 값 통일 후 원장 유니크 키+0.55
  2. 쓰기 주체 통합+0.40
  3. 가드 테이블+0.10
  4. 공통 행 잠금+0.10
  5. 커밋 후 발행+0.05
  6. 현행 유지0.00
  7. 주기적 정리-0.05

각 가중치를 5%p씩 흔든 141가지 조합에서 1위는 바뀌지 않았습니다. 2026년 9월 기준 제안 단계입니다.

결정

결과

기록과 자동화

배운 점

  1. 중복은 쓰는 쪽 여러 곳에서 막기보다, 저장하는 곳이 스스로 거부하게 만드는 방법을 먼저 봅니다.
    • 상황: 중복을 찾아 지워도 2시간이 안 돼 다시 생겼고, 쓰기 경로는 여러 서비스의 20여 곳에 걸쳐 있었습니다.
    • 한 일: 모든 생성 경로의 값을 먼저 통일한 뒤 원장에 유니크 키를 두는 안을 제안했습니다.
    • 결과: 쓰기 경로가 늘어도 같은 문제가 다시 생기지 않는 구조를 제안했습니다.
  2. 가중 결정표는 가중치를 흔들어도 결론이 버티는지 확인하고, 결론이 바뀌는 조건도 함께 적습니다.
    • 상황: 대안 7개를 기준 6개의 가중 결정표로 비교했습니다.
    • 한 일: 각 가중치를 5%p씩 흔든 141가지 조합을 모두 확인하고, 쓰기 주체 통합이 준비되면 두 안이 +0.55로 같은 점수가 된다는 조건을 적었습니다.
    • 결과: 141가지 조합 모두에서 1위가 바뀌지 않았고, 1·2위 차이는 가장 작을 때 0.10이었습니다.
  3. 원인은 코드를 읽고 추정하는 데서 멈추지 않고, 로그에서 실제로 겹친 기록을 확인한 뒤 정합니다.
    • 상황: 같은 정산 기록이 두 번 쌓이는 원인을 정해야 했습니다.
    • 한 일: 여러 서비스의 코드 근거를 조사하고, 4일 치 로그에서 두 서비스가 밀리초 단위로 같은 행을 각각 넣은 기록을 확인했습니다.
    • 결과: 서로 다른 잠금을 잡은 두 서비스의 경합과, 커밋 전에 보낸 이벤트라는 원인을 특정했습니다.