Skip to content
isdnetworks
Go back

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

(수정됨)

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

Table of contents

Open Table of contents

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

명령은 이렇게 생겼다.

php bin/job.php <명령> --job-id=<id>

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

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

원인 — 거치지 않는 스코프

실행 코드를 열었다.

private function executeOne($jobId)
{
    $job = $this->jobs->find($jobId);      // 직접 조회
    ...
}

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

// 워커 경로 (여기서만 씀)
$this->jobs->standby("fail_status IS NULL");

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
  php bin/job.php <명령> --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
중간 역할은 어느 쪽에도 없다