Skip to content
isdnetworks
Go back

단건 실행은 스코프를 안 거친다

job 표에 정체된 4,449건을 여러 서버에 나눠 재시도하는 스크립트를 설계했다. 단건 실행 명령이 이미 있어서 그것을 반복해 부르는 방식을 검토했다.

Table of contents

Open Table of contents

상황 — 단건 실행 명령의 성격

명령은 이렇게 생겼다.

./artisan <> --job-id=<id>

--job-id 로 도는 이 경로가 평소 워커와 같은 처리를 하는지부터 확인해야 했다.

같은 처리를 부르는 것으로 보이지만 들어가는 문이 다를 수 있고 문이 다르면 그 WHERE 도 다르다.

원인 — 거치지 않는 스코프

실행 코드를 열었다.

private function executeOne($jobId)
{
    $job = Job::find($jobId);      // 직접 조회
    ...
}

Job::find 로 바로 찾아서 처리에 들어간다.

// 워커 경로 (여기서만 씀)
Job::standby()->where('fail_status', null)->get();

standby 스코프도 fail_status 조건도 executeOne 은 안 탄다.

executeOne 안에서 차단하는 것이 있는지 봤더니 하나뿐이었다.

if ($job->completed_at) {
    return;      // 이미 완료면 건너뜀
}

completed_at 만 보고 나머지는 안 본다.

결과 — 두 가지가 편해졌다

이것을 알고 나니 계획이 단순해졌다.

fail_status 가 찍힌 건도 그대로 재시도되므로 사전에 되돌리는 갱신이 필요 없다. 조건을 안 거치는 것이 이 작업에서는 이득으로 작용했다.

이미 완료된 것이 섞여도 completed_at 가드에 걸려 무해하다. 완료 처리와 재시도의 순서를 엄격히 맞추지 않아도 된다는 뜻이다.

안전한 부분과 안 안전한 부분이 갈려 있어서 그 경계를 알고 써야 했다.

제약 — 범위로 넘기면

반대로 위험한 쪽도 같은 성질에서 나온다.

for id in $(seq 340000000 345000000); do ... done

seq 로 범위를 넘기면 그 안의 모든 작업이 실행되고 아무것도 안 걸러 준다.

실제로 그런 스크립트가 이미 있었다.

# id 범위로 작성돼 있음
345085389 ~ 345282019

확인해 보니 그 id 범위에 대상이 0건이었다.

스크립트 범위   345,085,389 ~ 345,282,019
실제 대상 범위   340,405,648 ~ 345,043,486

id 범위가 겹치지 않아서 아무것도 안 돌고 있었다. 편의를 위한 인자가 그대로 사고 경로가 되어 있었다.

조치 — 목록을 파일로 고정

방식을 바꿨다.

# DB 에서 추출
mysql -e "SELECT id FROM ... WHERE <조건>" > job_ids.txt

# 한 줄씩
while read -r id; do
  ./artisan <> --job-id="$id"
done < job_ids.txt

job_ids.txt 로 목록을 고정하고 그것만 돈다.

while read 대신 메모리에 담아 돌려도 되는데 파일로 한 데는 이유가 셋 있었다. 중단해도 어디까지 했는지 알면 이어갈 수 있고 무엇을 돌렸는지가 남으며 파일을 쪼개 여러 서버에 나눌 수 있다.

실행 시점에 SELECT 을 다시 하면 그 사이에 대상이 바뀔 수 있다. 파일과 그것을 뽑을 때 쓴 조건이 나중에 범위의 근거가 된다.

검증 — 0건이 조용했다

executeOne 의 성공과 실패에 무엇이 바뀌는지도 봤다.

성공  →  완료 시각 설정
실패  →  로그 갱신, 시도 횟수 증가, 실패 상태 재매핑

실패해도 fail_status 가 남으므로 돌린 뒤에 결과를 집계할 수 있다.

SELECT fail_status, COUNT(*) FROM jobs WHERE id IN (<목록>) GROUP BY 1;

이 건에서 가장 걸린 것은 다른 쪽이었다.

스크립트 실행  →  정상 종료
처리 건수      →  0

잘못된 범위로 돌리면 0건이 처리되는데 종료 코드가 0이라 성공처럼 보인다.

앞서 여러 번 만난 것과 같은 모양이다. 종료 코드가 아니라 건수를 봐야 실제로 무엇을 했는지 알 수 있어서 돌린 뒤에 예상 건수와 대조하는 것을 절차에 넣었다.

정리


Share this post on:

Previous Post
백 초쯤 지나면 끊기던 연결
Next Post
중간 역할은 어느 쪽에도 없다