특정 판매자의 상품 1만 2천 건을 전부 판매중지로 바꾸는 명령을 만들어 돌렸다. 상태는 잘 바뀌었는데 연동 작업 테이블에 12만 건이 쌓였다.
Table of contents
Open Table of contents
저장마다 도는 관찰자
명령은 대상을 조각으로 나눠 돌면서 상태를 바꾸고 저장하는 형태였다. 모델에 붙은 관찰자가 상태 변경을 감지하면 연결된 채널 수만큼 작업을 만들고 있었다.
상품 하나당 연결 채널이 평균 열 개이므로 곱하면 12만 건이 된다. 화면에서 한 건을 바꿀 때는 맞는 동작인데 그 설계가 대량 처리에서도 그대로 돌면서 건수만큼 반복된 것이다.
만들어진 것과 필요한 것
이 작업에서 필요한 것은 각 채널에 판매중지를 알리는 것이었다. 관찰자가 만든 것은 갱신 작업이었다.
유형도 다르고 갱신 작업은 상품 정보를 다시 밀어 넣으므로 판매중지 의도와 맞지도 않았다. 정리하면 필요 없는 것이 대량으로 생기고 필요한 것은 안 생긴 상태였다.
조용히 저장하고 직접 만들기
모델 이벤트를 건너뛰고 저장하는 방법이 있어서 그것을 쓰되 이벤트가 하던 일을 직접 해야 했다. 상태 변경은 건별 저장 대신 쿼리 빌더의 한 문장으로 바꿨는데 그쪽은 모델 이벤트 자체가 없다.
필요한 작업은 조건을 걸어 한 번의 삽입 문장으로 만들었다. 연결이 실제로 살아 있는 것만 대상으로 했는데 채널 식별자가 없으면 상대에 등록된 적이 없어 중지할 대상 자체가 없기 때문이다.
조용히 사라지는 이력
관찰자를 건너뛰면 이력도 안 남는데 이것이 조용한 손실이라 더 위험하다. 나중에 누가 언제 이것을 내렸는지에 답할 수 없게 된다.
그래서 이력을 직접 삽입하되 담당자 항목은 비워 뒀다. 실제로 사람이 한 것이 아닌데 특정 담당자 번호를 넣으면 감사 기록이 왜곡되기 때문이다.
배치를 만들 때 보는 것
이 일 뒤로 대량 처리 명령을 만들 때 두 가지를 먼저 본다. 모델에 관찰자와 부팅 훅과 이벤트 리스너와 트레이트 중 무엇이 붙어 있는지를 하나씩 열어 저장 시 무엇이 일어나는지를 확인한다.
그리고 건별 저장을 한 문장으로 바꿀 수 있는지를 보는데 바꿀 수 있으면 모델 이벤트 문제가 함께 사라진다. 대신 이벤트가 하던 일 중 필요한 것을 관찰자 코드를 그대로 읽어 목록으로 만들어야 하고 기억으로 옮기면 빠진다. 실제로 처음에는 이력 삽입을 빠뜨렸다가 리뷰에서 지적받았다.
정리
- 관찰자는 저장 한 번마다 돈다
- 건별로 맞는 동작이 대량 처리에서 건수만큼 반복된다
- 만들어진 것과 필요한 것이 다를 수 있다
- 필요 없는 것이 대량으로 생기고 필요한 것은 안 생긴다
- 조용한 저장이나 쿼리 빌더로 모델 이벤트를 건너뛴다
- 건너뛰면 이벤트가 하던 일을 직접 해야 한다
- 배치 처리의 이력에는 담당자를 비워 둔다
- 필요한 처리 목록은 관찰자 코드를 읽어 만든다