Skip to content
isdnetworks
Go back

매칭은 양방향으로 확인한다

정산에서 매칭 안 된 건이 5,987건 나왔다. 원본 파일과 우리 데이터를 대조하는 작업이었다.

Table of contents

Open Table of contents

증상 — 미매칭 5,987건

첫 가설은 단순했다.

특정 컬럼이 비어 있어서 매칭이 안 된다

엑셀을 열면 빈 칸이 바로 눈에 보이니 NULL 쪽으로 먼저 갔다.

그런데 NULL 인 행을 COUNT 해 보니 그렇게 많지 않았다. 그것을 전부 채워도 대부분이 그대로 남는다는 뜻이라 다른 원인을 찾아야 했다.

원인 — 다섯 겹으로 쌓인 규칙

매칭 코드를 읽으니 JOIN 하나로 끝나는 구조가 아니었다.

일부 채널은 특정 번호로 매칭하는 것을 아예 건너뛰고 있었다.

if (in_array($channel, self::SKIP_ORDER_ID_MATCH)) {
    // 이 번호로는 매칭 안 함
}

SKIP_ORDER_ID_MATCH 에 든 채널은 주문 번호가 아닌 다른 키로 맞춰야 한다.

일부 채널은 옵션 식별자를 인코딩해 특정 형식으로 넣고 있었다.

_F{인코딩된 값}

원본 파일에 _F 로 시작하는 그 형식이 들어 있으니 디코드해야 우리 값과 맞는다.

교환 주문은 원주문이 아니라 재발송 항목에 붙어야 했고 후보가 여럿이면 실패로 보는 분기도 있었다.

if ($candidates->count() !== 1) {
    // 매칭 실패
}

$candidates 가 정확히 하나로 특정될 때에만 매칭으로 통과시키고 있었다.

마지막으로 이관된 상품은 두 표의 식별자 체계가 달랐다.

연동 테이블   신 구조 식별자
항목 테이블   옛 구조 식별자

연동 테이블항목 테이블 을 직접 JOIN 할 수 없어 한 단계를 거쳐야 한다.

처음 본 것은 엑셀 컬럼과 DB 컬럼의 대응 하나였는데 실제로는 채널 예외와 인코딩과 교환 분기와 유일성과 이관 불일치가 그 위에 얹혀 있었다. 컬럼 매핑만 보고 있었으니 나머지 네 겹은 보일 수가 없었다.

조치 — 순서대로 좁혔다

다섯 규칙을 한꺼번에 걸지 않고 순서대로 적용했다.

1. 채널별로 나눈다
2. 인코딩된 키를 디코드한다
3. 교환 건은 재발송으로 대상을 바꾼다
4. 이관 건은 한 단계 거쳐 조인한다
5. 양방향 1:1 확인

각 단계에서 매칭되는 건수가 얼마나 느는지를 COUNT(*) 로 그때마다 셌다.

다섯을 한꺼번에 걸어 버리면 어느 규칙이 몇 건을 설명했는지 알 수 없다. 하나씩 벗겨 내니 규칙마다의 기여가 숫자로 남았다.

검증 — 양방향 1:1 확인

매칭 스크립트에 규칙을 하나 두었다.

-- 방향 A: 원본 → 우리 것
SELECT COUNT(*) FROM ours WHERE key = <원본 키>;
-- 정확히 1이어야 함

-- 방향 B: 우리 것 → 원본
SELECT COUNT(*) FROM source WHERE key = <우리 키>;
-- 정확히 1이어야 함

ours 쪽과 sourceCOUNT(*) 가 둘 다 1일 때에만 매칭으로 인정한다.

ours 쪽만 재고 넘어가면 통과와 오염을 가르지 못하고 두 방향이 같은 답을 낼 때에만 짝이 하나로 정해진다.

주의 — 한 방향만 보면 다대일이 통과한다

왜 한쪽으로 부족한지는 이런 모양에서 그대로 드러난다.

원본 A  →  우리 것 X   (1건, 통과)
원본 B  →  우리 것 X   (1건, 통과)

AB 도 각각은 1건이라 원본 쪽에서 보면 둘 다 통과한다.

반대 방향으로 보면 그 자리에서 잡힌다.

우리 것 X  →  원본 A, B   (2건, 실패)

X 하나에 AB 가 둘 다 붙어 있으니 실패로 걸린다.

한 방향만 봤으면 이것이 통과해 데이터가 오염된다. 정산에서 그것은 같은 금액이 두 번 잡힌다는 뜻이었다.

결과 — 남은 여섯 건

돌린 결과는 이랬다.

전체       5,987건
해결       5,981건  (99.9%)
남음           6건

남은 여섯 건은 각자 개별 사정이 있는 것들이라 손으로 하나씩 봤다.

단계별로 세어 두었기 때문에 그 여섯 건이 어느 단계에서 막혔는지도 함께 알 수 있었다. 처음에 세운 NULL 가설은 눈에 보이는 것이었고 실제 원인은 코드를 읽어야 나오는 쪽에 있었다.

정리


Share this post on:

Previous Post
24시간 멈추면 안 되는 시스템을 맡는다는 것
Next Post
먼저 뜬 것이 나중에 내려가야 했다