파일을 처리하는 cron 배치가 있었고 각 파일이 어디까지 처리됐는지를 MySQL 에 상태 컬럼으로 두고 있었다. 그런데 그 상태와 실제 파일의 상황이 자주 어긋났다.
Table of contents
Open Table of contents
원인 — 상태와 실제의 어긋남
어긋나는 이유는 파일을 옮기는 것과 상태를 UPDATE 하는 것이 별개의 동작이기 때문이었다. 둘 사이에서 프로세스가 죽으면 한쪽만 반영된 채로 남는다.
두 곳을 항상 맞추려면 한 트랜잭션으로 묶어야 하는데 파일과 MySQL 은 그렇게 묶이지 않는다. 그래서 상태를 따로 두는 방식 자체를 바꾸기로 했다.
자리가 곧 상태
파일이 어느 폴더에 있는지가 곧 그 파일의 상태가 되게 만들었다. 대기와 처리 중과 완료와 실패로 폴더를 나누고 처리가 진행될 때마다 파일을 옮긴다.
이렇게 하면 상태를 따로 기록하지 않으므로 어긋날 것이 없다. 지금 무엇이 어디까지 됐는지도 폴더를 열어 보면 바로 보인다.
옮기는 것이 한 번만 성공한다
여러 워커가 동시에 돌 때 같은 파일을 두 번 집는 문제도 이 방식이 함께 해결했다. 같은 파일을 rename 하려고 하면 먼저 부른 쪽이 가져가고 뒤에 부른 쪽은 원본이 이미 없어서 실패한다.
그래서 별도의 잠금 장치 없이도 한 파일이 한 번만 처리된다. 상태를 자리로 바꾸면서 동시성 문제까지 같이 정리된 셈이었다.
처리 중에 남은 것 되돌리기
처리 중 폴더에 파일이 오래 남아 있으면 그 처리가 중간에 죽었다는 뜻이다. 그래서 mtime 이 일정 시간을 넘은 것은 대기 폴더로 되돌리게 했다.
되돌리기 전에 그 처리가 두 번 돌아도 되는지를 확인했는데 안 되면 되돌리는 대신 사람이 보게 해야 한다. 되돌리는 것이 안전한지는 처리 내용에 달려 있다.
실패 보관과 기준 정하기
실패한 파일은 오류 내용을 적은 파일과 함께 실패 폴더에 두었다. 고쳐서 다시 넣을 때 무엇 때문에 실패했는지가 그 자리에 있어야 한다.
완료된 것은 날짜로 나누어 쌓았고 DB에도 이력을 남기되 무엇이 기준인지를 정해 뒀다. 둘 다 있을 때 어긋나면 파일 쪽을 따른다는 것을 명시해야 나중에 헷갈리지 않는다.
정리
- 상태를 따로 기록하면 실제와 갈린다
- 파일 옮기기와 상태 변경이 별개 동작이라 어긋난다
- 파일이 어느 폴더에 있는지가 곧 상태가 되게 한다
rename은 한 번만 성공하므로 잠금이 필요 없다- 파일과
MySQL은 한 트랜잭션으로 묶이지 않는다 - 처리 중에 오래 남은 것을
mtime으로 되돌린다 - 되돌리기 전에 두 번 처리돼도 되는지 확인한다
MySQL에도 남기되 무엇이 기준인지 정한다