이력 테이블에 이렇게 찍혀 있었다.
3월 14일 23:03 출고 생성 · 송장 43937812472
Table of contents
Open Table of contents
증상 — 이력과 자료가 안 맞았다
그런데 order_exports 에는 1건뿐이었다.
[이력] 출고 생성 (3월 14일)
[출고] 1건, created_at 3월 2일
생성됐다는데 created_at 이 3월 2일인 것 하나뿐이었다.
문구가 자료와 어긋나면 둘 중 하나는 사실이 아니다. 이력은 사람이 아니라 코드가 남기는 것이라 처음에는 그쪽을 믿었다.
검증 — 시각 두 개를 봤다
그 1건의 두 시각을 함께 봤다.
SELECT created_at, updated_at, delivery_tracking_number
FROM order_exports WHERE id = ?;
created_at 3월 2일 ...
updated_at 3월 14일 23:02
송장 43937812472
updated_at 이 3월 14일이고 송장이 이력에 찍힌 것과 같았다.
created_at 과 updated_at 이 며칠 차이가 났고 이력 시각은 뒤쪽과 붙어 있었다. 그 시점에 일어난 것은 생성이 아니라 갱신이었다.
원인 — 하위 층 기준의 문구
이렇게 놓으니 앞뒤가 맞아떨어졌다.
새 출고를 만든 게 아니라
기존 출고의 송장과 출고시각을 덮어씀
created_at은 그대로
updated_at만 갱신
이력 문구는 생성인데 실제로는 갱신이었다.
이력 문구를 남기는 자리를 grep 으로 찾았다.
$this->addHistory($item, '출고 생성 · 송장 ' . $number);
addHistory 가 붙어 있는 대상이 하위 항목이었다.
export_item 새로 생김 ← "생성"
export 기존 것 갱신
export_item 기준으로는 그 문구가 생성인 것이 맞았다.
같은 사건이라도 어느 층에서 보느냐에 따라 생성이기도 하고 갱신이기도 하다. 문구가 틀린 것이 아니라 그것이 무엇에 대한 것인지를 내가 잘못 읽은 것이었다.
교훈 — 같은 측정의 반복
이 건을 조사하면서 오진을 두 번 했다.
[1차] 출고 1건 → 재출고 안 함
[2차] 다시 세어 봄 → 여전히 1건 → 재출고 안 함
같은 방법으로 두 번 세면 두 번 다 같은 답이 나온다.
세 번째 확인에서는 아예 다른 것을 봤다.
[1·2차] 건수
[3차] created_at 대 updated_at, 그리고 이력
세는 것에서 대조하는 것으로 바꾸니 그때 판정이 갈렸다.
COUNT 라는 측정 자체가 틀렸는데 그것을 반복해서 확인한 것이었다. 같은 측정을 반복하는 것은 확인이 아니다.
판단 기준 — 두 시각의 대조
이 유형의 판별법을 적어 뒀다.
SELECT created_at, updated_at FROM order_exports WHERE order_id = ?;
같으면 손댄 적 없음
다르면 뭔가 갱신됨 ← 이력으로 확인
출고가 1건뿐인 것만 보고 재출고를 안 했다고 단정하지 않는다.
이력과 두 시각을 반드시 대조한다. 건수만 세고 있으면 덮어쓰기가 있었는지 없었는지가 아예 안 갈린다.
주의 — 오염된 경과일
이 덮어쓰기가 created_at 을 쓰는 다른 계산에 영향을 줬다.
$elapsed = now()->diffInDays($export->created_at);
if ($elapsed > 7) { /* 기한 초과 */ }
created_at 기준으로 경과일을 재는 자리가 있었다.
created_at 3월 2일
재발송 3월 14일
경과일 계산 → 12일
재발송 직후인데 elapsed 가 12일이라 이미 기한 초과로 잡힌다.
덮어쓰기 설계 때문에 created_at 이 가장 최근 출고 시점이 아니게 됐고 그것을 기준으로 재는 계산이 전부 틀어졌다.
grep -rn "export->created_at\|exports.created_at" app/
created_at 을 쓰는 자리를 전부 찾아 그 뜻이 유효한지 확인했다.
덮어쓰기가 있는 표에서 그 컬럼은 최초 생성이지 최근 동작이 아니다. 한 곳의 설계 선택이 무관해 보이는 곳을 깬다.
정리
- 이력 문구가 생성이어도 실제로는 갱신일 수 있다
- 어느 층에서 보느냐에 따라 생성이기도 갱신이기도 하다
- 건수만 세면 안 갈린다
created_at과updated_at을 대조한다- 같은 측정을 반복하는 것은 확인이 아니다
- 다른 방법으로 봐야 판정이 갈린다
- 덮어쓰기 설계는
created_at의 뜻을 바꾼다 - 그것을 기준으로 재는 경과일 계산이 오염된다
- 덮어쓰기가 있는 표는 그 컬럼 사용처를 전부 확인한다