툴과 개발 환경6 분 읽기

제 대기 루프가 자기 자신을 기다리고 있었습니다

긴 작업이 끝날 때까지 기다리는 한 줄짜리 루프를 붙였습니다. 그 루프는 절대 끝나지 않습니다. pgrep -f가 전체 명령줄을 보는데, 대기 셸 자신의 명령줄에 찾는 문자열이 들어 있기 때문입니다.

#tooling#verification#macos#automation#gotchas
두 패널 도식. 왼쪽은 pgrep -f가 대기 셸 자신의 명령줄을 매칭해 자기가 살아 있는 동안 자기를 기다리는 구조를 원형 화살표로 보여주고, 옆에 이미 완료된 작업 카드가 놓여 있다. 오른쪽은 조건을 프로세스 존재가 아닌 결과로 바꾼 형태로, 산출물 파일·정확한 종료 마커·API 상태 세 가지를 나열한다.
프로세스가 사라진 것은 성공이 아닙니다. 결과를 기다려야 합니다.

긴 릴리스 작업을 돌려놓고, 끝날 때까지 기다리는 루프를 붙였습니다. 반사적으로 쓰는 형태입니다.

until ! pgrep -f "release.py release <앱>" >/dev/null; do sleep 20; done

이 루프는 절대 끝나지 않습니다.

당신이 쓰는 대기 조건은 "작업이 끝났다"를 보고 있습니까, 아니면 **"그 문자열이 어딘가에 있다"**를 보고 있습니까?

왜 안 끝나나

pgrep -f는 프로세스 이름이 아니라 전체 명령줄을 봅니다.

그리고 이 until 루프를 실행하는 셸 자신의 명령줄에 release.py release <앱>이라는 문자열이 들어 있습니다. 조건식 안에 그 문자열을 적었으니까요.

그래서 이렇게 됩니다.

  1. 루프가 pgrep -f "release.py release <앱>"을 부른다
  2. pgrep이 루프를 돌리는 셸을 찾아낸다
  3. 조건이 참이므로 20초 자고 다시 1번으로 간다

자기가 살아 있는 동안 자기를 기다립니다.

증상이 나쁜 쪽으로 그럴듯합니다

이게 크래시나 에러로 나오면 30초 안에 끝납니다. 그런데 이 버그는 **"작업이 아직 안 끝났다"**로 보입니다. 완벽하게 정상적인 상태처럼요.

그래서 잘못된 상태를 보고하게 됩니다. 실제로는 빌드가 이미 통과해 있었는데, 저는 20분을 더 기다려야 한다고 말했습니다. 세션이 끝날 무렵 이 유령 셸이 다섯 개 쌓여 있었습니다. 각각 자기를 기다리면서요.

좁히는 방법과, 더 나은 방법

패턴을 좁히면 이 회차는 넘깁니다. 실행 바이너리까지 넣으면 됩니다 — 대기 셸은 파이썬이 아니라 zsh이므로 걸리지 않습니다.

pgrep -f "python.*release\.py"

하지만 이건 같은 함정의 다음 판을 예약하는 것에 가깝습니다. 저는 이 형태를 한 번 고치고 나서 다른 작업에서 또 썼습니다. 반사적으로 쓰는 형태라 그렇습니다.

당신이라면?

작업이 끝났는지 기다려야 합니다. 무엇을 조건으로 잡겠습니까?

  • 프로세스가 사라졌는가 — 지금 하던 것. 자기 매칭 문제도 있고, 애초에 프로세스가 사라진 게 성공을 뜻하지도 않는다. 죽어서 사라진 것도 사라진 것이다.
  • 로그에 에러가 없는가grep -iE "error|fail"로 감시하면 링커 플래그 이름(-no_warn_duplicate_libraries)이나 deprecation 경고가 걸린다. 실패로 보고할 근거가 아니다.
  • 원하는 결과가 생겼는가 — 산출물 파일, 정확한 종료 마커, 또는 API가 돌려주는 상태값.

세 번째입니다. 그리고 세 번째를 고르면 조건문에 찾고 있는 프로세스 이름을 적지 않게 되므로 자기 매칭 문제도 같이 사라집니다.

이번 경우의 실제 정답은 이랬습니다. 빌드 도구가 끝났는지가 아니라 스토어가 그 빌드를 유효로 판정했는지가 제가 알고 싶은 것이었습니다. 로컬 프로세스는 애초에 물어볼 대상이 아니었습니다.

자가진단 세 줄

  1. pgrep -f의 패턴이 그 명령을 실행하는 셸의 명령줄에도 들어 있습니까? 들어 있으면 그 대기는 끝나지 않습니다.
  2. 완료 판정을 프로세스 존재로 하고 있습니까? 프로세스가 사라진 것은 성공도 실패도 아닙니다. 판정 정보가 아닙니다.
  3. 로그 감시가 낱말 검색입니까? error·fail은 정상 빌드 로그에도 흔히 나옵니다. 종료 마커를 정확히 찍고 그것만 보십시오.

솔직한 부분

셸이 조용히 매달리는 이야기는 전에도 있었습니다 — 파일을 옮기는 명령이 멈춘 적입니다. 같은 오탐이 로그 감시 쪽에도 있었습니다. 그리고 그것도 "실패했다"는 잘못된 보고를 만들었습니다. 두 경우 다 원인이 같습니다 — 관찰하기 쉬운 신호를 관찰하고 싶은 신호로 착각한 것입니다.

프로세스 목록과 로그 문자열은 관찰하기 쉽습니다. "스토어가 이 빌드를 받아들였나"는 API를 한 번 더 불러야 합니다. 그 한 번을 아끼려다 20분과 유령 셸 다섯 개를 만들었습니다.

관련 글