긴 릴리스 작업을 돌려놓고, 끝날 때까지 기다리는 루프를 붙였습니다. 반사적으로 쓰는 형태입니다.
until ! pgrep -f "release.py release <앱>" >/dev/null; do sleep 20; done이 루프는 절대 끝나지 않습니다.
당신이 쓰는 대기 조건은 "작업이 끝났다"를 보고 있습니까, 아니면 **"그 문자열이 어딘가에 있다"**를 보고 있습니까?
왜 안 끝나나
pgrep -f는 프로세스 이름이 아니라 전체 명령줄을 봅니다.
그리고 이 until 루프를 실행하는 셸 자신의 명령줄에 release.py release <앱>이라는 문자열이 들어 있습니다. 조건식 안에 그 문자열을 적었으니까요.
그래서 이렇게 됩니다.
- 루프가
pgrep -f "release.py release <앱>"을 부른다 pgrep이 이 루프를 돌리는 셸을 찾아낸다- 조건이 참이므로 20초 자고 다시 1번으로 간다
자기가 살아 있는 동안 자기를 기다립니다.
증상이 나쁜 쪽으로 그럴듯합니다
이게 크래시나 에러로 나오면 30초 안에 끝납니다. 그런데 이 버그는 **"작업이 아직 안 끝났다"**로 보입니다. 완벽하게 정상적인 상태처럼요.
그래서 잘못된 상태를 보고하게 됩니다. 실제로는 빌드가 이미 통과해 있었는데, 저는 20분을 더 기다려야 한다고 말했습니다. 세션이 끝날 무렵 이 유령 셸이 다섯 개 쌓여 있었습니다. 각각 자기를 기다리면서요.
좁히는 방법과, 더 나은 방법
패턴을 좁히면 이 회차는 넘깁니다. 실행 바이너리까지 넣으면 됩니다 — 대기 셸은 파이썬이 아니라 zsh이므로 걸리지 않습니다.
pgrep -f "python.*release\.py"하지만 이건 같은 함정의 다음 판을 예약하는 것에 가깝습니다. 저는 이 형태를 한 번 고치고 나서 다른 작업에서 또 썼습니다. 반사적으로 쓰는 형태라 그렇습니다.
당신이라면?
작업이 끝났는지 기다려야 합니다. 무엇을 조건으로 잡겠습니까?
- 프로세스가 사라졌는가 — 지금 하던 것. 자기 매칭 문제도 있고, 애초에 프로세스가 사라진 게 성공을 뜻하지도 않는다. 죽어서 사라진 것도 사라진 것이다.
- 로그에 에러가 없는가 —
grep -iE "error|fail"로 감시하면 링커 플래그 이름(-no_warn_duplicate_libraries)이나 deprecation 경고가 걸린다. 실패로 보고할 근거가 아니다. - 원하는 결과가 생겼는가 — 산출물 파일, 정확한 종료 마커, 또는 API가 돌려주는 상태값.
세 번째입니다. 그리고 세 번째를 고르면 조건문에 찾고 있는 프로세스 이름을 적지 않게 되므로 자기 매칭 문제도 같이 사라집니다.
이번 경우의 실제 정답은 이랬습니다. 빌드 도구가 끝났는지가 아니라 스토어가 그 빌드를 유효로 판정했는지가 제가 알고 싶은 것이었습니다. 로컬 프로세스는 애초에 물어볼 대상이 아니었습니다.
자가진단 세 줄
pgrep -f의 패턴이 그 명령을 실행하는 셸의 명령줄에도 들어 있습니까? 들어 있으면 그 대기는 끝나지 않습니다.- 완료 판정을 프로세스 존재로 하고 있습니까? 프로세스가 사라진 것은 성공도 실패도 아닙니다. 판정 정보가 아닙니다.
- 로그 감시가 낱말 검색입니까?
error·fail은 정상 빌드 로그에도 흔히 나옵니다. 종료 마커를 정확히 찍고 그것만 보십시오.
솔직한 부분
셸이 조용히 매달리는 이야기는 전에도 있었습니다 — 파일을 옮기는 명령이 멈춘 적입니다. 같은 오탐이 로그 감시 쪽에도 있었습니다. 그리고 그것도 "실패했다"는 잘못된 보고를 만들었습니다. 두 경우 다 원인이 같습니다 — 관찰하기 쉬운 신호를 관찰하고 싶은 신호로 착각한 것입니다.
프로세스 목록과 로그 문자열은 관찰하기 쉽습니다. "스토어가 이 빌드를 받아들였나"는 API를 한 번 더 불러야 합니다. 그 한 번을 아끼려다 20분과 유령 셸 다섯 개를 만들었습니다.