개발 slecs

병렬 워크트리 생성 시 발생하던 레이스 컨디션 수정

목차

brainstorm 실행 파이프라인을 손보다가 발견한 미묘한 버그를 고쳤다. 여러 프로세스가 동시에 git worktree를 생성/삭제할 때, 이전 작업이 온전히 정리되지 않은 상태로 새 worktree 추가를 시도하면서 간헐적으로 실패하거나 상태가 꼬이는 문제였다.

git worktree와 orphan 디렉터리 문제

git worktree는 한 저장소 내에서 여러 개의 작업 디렉터리를 동시에 유지할 수 있는 기능이다. 내가 다루는 brainstorm 처리 파이프라인에서도 병렬로 여러 실험을 돌릴 때 각 실험마다 독립된 워크트리를 만들어 서로 간섭하지 않게 한다. 그런데 이게 의외로 까다로운 부분이 있다.

worktree를 삭제할 때, 파이썬에서 단순히 디렉터리를 rm -rf로 지우는 것만으로는 부족할 수 있다. git 내부 메타데이터가 남아있을 수 있기 때문이다. 특히 여러 프로세스가 거의 동시에 worktree를 추가·제거하는 상황에서는 다음 같은 일이 벌어진다:

  • 프로세스 A가 worktree를 삭제했지만, 실제 파일시스템은 아직 정리 중
  • 프로세스 B가 같은 이름으로 새 worktree를 추가하려고 시도
  • git이 "이 worktree는 이미 존재한다"고 판단하거나, 파일이 아직 있어서 충돌

결과적으로 "디렉터리가 이미 존재한다", "worktree 잠금 파일이 남아있다" 같은 에러가 간헐적으로 발생했다.

레이스 컨디션의 일반적 패턴

이런 류의 버그는 로컬 개발에서는 재현이 어렵다. 한두 번 실행하면 잘 동작하기 때문이다. 그런데 병렬 작업이 많아지거나 CI/CD 환경에서 수십 개의 작업을 동시에 돌릴 때 갑자기 나타난다.

일반적으로 파일시스템 + 메타데이터 동기화 문제는 다음 두 가지 대안이 있다:

접근법 장점 단점
재시도 루프 + 지수 백오프 간단, 기존 코드 수정 최소 시간이 걸릴 수 있음, 본질적 해결 아님
사전 정리 (pre-cleanup) 확실함, 예측 가능 불필요한 삭제가 있을 수 있음

나는 후자를 선택했다. worktree를 추가하기 전에, 혹시 그 이름의 orphan 디렉터리나 메타데이터가 남아있으면 먼저 깨끗이 제거하는 식으로.

수정의 의도와 구조

# brainstorm_exec.py 의사 코드
def ensure_clean_worktree(worktree_name):
    worktree_path = f".git/worktrees/{worktree_name}"

    # 1. git 메타데이터 정리
    if os.path.exists(worktree_path):
        shutil.rmtree(worktree_path)

    # 2. 파일시스템 디렉터리 정리  
    actual_path = f"worktrees/{worktree_name}"
    if os.path.exists(actual_path):
        shutil.rmtree(actual_path)

    # 3. git worktree add 실행 (이제 깨끗한 상태)
    git_worktree_add(worktree_name)

핵심은 "먼저 치우고 나서 만든다"는 철학이다. git의 상태와 파일시스템 상태를 명시적으로 동기화해준다.

배운 점과 팀에 미친 영향

이 수정이 의미 있는 이유는 다음과 같다:

  • 안정성 향상: 병렬 실행 환경에서 간헐적 실패가 줄어든다. 이전에는 "운이 좋을 때"만 성공했다.
  • 디버깅 용이성: 누군가 "왜 여기서 자꾸 실패해?"라고 물어봤을 때, 원인이 명확해진다.
  • 예측 가능성: 여러 팀원이 이 스크립트를 사용하는데, 이제 "항상 같은 방식으로 동작한다"는 신뢰가 생긴다.

특히 CI/CD나 대량 배치 작업에서는 이런 작은 race condition이 누적되면 전체 파이프라인 신뢰도를 떨어뜨린다. 팀원들이 "어, 이건 왜 또 실패했어?"라고 묻는 시간과 비용도 줄어든다.

일반화할 수 있는 패턴

파일시스템 + 메타데이터 조합에서 일어나는 레이스 컨디션은 git worktree 말고도 다양한 곳에서 나타난다:

  • 도커 컨테이너 volume 정리
  • 데이터베이스 임시 테이블
  • 캐시 디렉터리 관리
  • 잠금 파일(lock file)

이런 경우들도 같은 원칙을 따른다: 명시적인 사전 정리 → 작업 수행. 이렇게 하면 간헐적 실패를 대부분 피할 수 있다.


🛒 이 글과 어울리는 추천 상품

*위 링크는 쿠팡파트너스 활동의 일환이며, 일정액의 수수료를 제공받을 수 있습니다.

댓글 0

첫 댓글 달아줘.