작업이 여러 건 멈춰 있었고 대상은 이미 외부에서 영구 중단된 것들이었다. 다시 보내도 실패할 것이 뻔했으므로 jobs 를 UPDATE 로 완료 처리하고 끝내려 했다.
UPDATE jobs SET completed_at = NOW() WHERE id IN (...);
Table of contents
Open Table of contents
멈춘 작업과 완료 처리
넣기 전에 대상 쪽을 봤다.
SELECT status FROM distribution_products WHERE id IN (...);
stop_fail 과 update_fail 이었다. jobs 를 완료로 바꾸면 이 status 도 바뀌는지가 물음이었다.
grep 으로 코드를 따라가니 실행기의 완료 콜백에서만 그 기록이 일어났다.
// DistributionProductJobExecutor.php
private function jobCompleted($job, $result)
{
$this->addDistributionProductStatus($channel, $productId, $job, $result);
}
private function addDistributionProductStatus($channel, $productId, $job, $result)
{
$dp->status = "{$job}_{$result}"; // stop_success
$dp->save();
}
jobs 를 직접 UPDATE 하면 jobCompleted 가 안 돌아 이 체인이 통째로 건너뛴다.
상태를 기록하는 자리
status 를 쓰는 자리를 grep 으로 찾아보니 addDistributionProductStatus 하나뿐이고 다른 데서는 읽기만 했다.
값의 형식은 "{$job}_{$result}" 로 작업 종류와 결과를 밑줄로 붙인 것이었다. 직접 넣으려면 그 형식을 정확히 맞춰야 하고 안 맞추면 읽는 쪽에서 걸린다.
작업만 손대면 상태가 엇갈린다
작업 행만 완료로 바꾸면 작업은 끝났는데 대상 상태는 처리 전 값으로 남는다. 두 값이 엇갈린 채로 남으면 나중에 어느 쪽을 믿을지가 문제가 된다.
이 엇갈림은 오류로 드러나지 않고 SELECT 했을 때만 보인다. 그래서 시간이 지난 뒤에 다른 사람이 발견하고 원인을 다시 찾게 된다.
두 선택지와 실패 경로의 길이
선택지는 job 과 대상을 함께 직접 UPDATE 하는 것과 실행기를 태워서 정상 경로로 끝내는 것 둘이었다. 앞의 것은 빠르고 뒤의 것은 부수 처리까지 함께 돈다.
실패 경로를 따라가 보니 성공 경로보다 단계가 하나 더 있었는데 실패 사유를 분류하는 단계였다. 그 분류값까지 맞추기 어려워서 이 건은 직접 갱신으로 가되 두 표를 한 트랜잭션에서 함께 UPDATE 하기로 했다.
조치 전에 정해야 하는 이유
이 결정은 첫 쿼리를 돌리기 전에 해야 했다. 작업 행을 먼저 완료로 바꾸면 실행기로 태우는 선택지가 사라지기 때문이다.
같은 모양의 판단이 이후에도 반복됐는데 확인 방법은 매번 같았다. 그 상태 컬럼을 누가 쓰는지 코드에서 찾아보면 조치 전에 알 수 있다.
정리
job을UPDATE로 완료 처리해도 대상 상태는 안 바뀐다- 상태는 실행기의 콜백 경로에서만 기록된다
- 상태 값에 작업 종류와 결과가 붙는 형식이 있다
- 실패 경로에는 분류 단계가 하나 더 있다
- 작업만 손대면 엇갈린 상태가 남는다
- 엇갈림은 오류로 안 드러나고
SELECT할 때만 보인다 - 조치 전에 정한다. 첫 갱신이 선택지를 없앤다
- 상태 컬럼을 누가 쓰는지
grep으로 찾으면 미리 알 수 있다