하루 병합이 열 배로 늘면 팀 저장소는 어디부터 막히나

동딩
2026.10.24·3분 읽기·

숫자 셋을 재면 어디가 먼저 막히는지 나온다

하루 병합 수, CI 한 번에 걸리는 시간, 실행 한 번의 잡 수를 잽니다. R에 저장소를, ci.yml에 기본 브랜치에서 도는 워크플로 파일 이름을 넣습니다. 잡 수는 Actions 탭에서 실행 하나를 열면 보입니다.

bash
R=my-org/my-repo
# 지난 7일 병합 PR 수(÷7 = 하루 병합 수)
gh pr list -R $R --state merged --search "merged:>=2026-10-17" \
--limit 1000 --json number --jq length
# CI 시간 중앙값(분)
gh run list -R $R -w ci.yml -b main -s success -L 30 \
--json startedAt,updatedAt \
--jq '[.[]|((.updatedAt|fromdate)-(.startedAt|fromdate))/60|floor]|sort|.[length/2|floor]'

머지 큐는 PR을 main 최신본과 합친 상태로 CI를 한 번 더 돌려, 통과한 것만 차례로 병합하는 대기열입니다. 검사를 하나씩 돌리면 CI가 20분인 저장소는 하루 72건이 끝입니다.

막히는 곳하루 한도넘으면
머지 큐1,440 ÷ CI 분 × 동시 빌드 수PR이 다음 날로 밀립니다
CI 러너동시 잡 한도 × 1,440 잡-분잡이 러너를 기다립니다
git 체크아웃러너 시간 중 클론 몫잡마다 클론이 길어집니다

잡-분은 러너 한 대가 1분 일한 양입니다. 쓰는 양은 하루 병합 수 × PR 하나가 도는 CI 횟수 × 잡 수 × 잡 평균 분입니다.

GitHub Team 요금제는 표준 러너를 동시에 60개까지 돌리니 하루 86,400잡-분입니다.

클론 몫까지 재려면

실행 하나의 id를 ID에 넣고 체크아웃 단계 시간의 합(초)을 뽑아 잡 시간의 합과 견줍니다.

bash
gh run view $ID -R $R --json jobs --jq '[.jobs[].steps[]|select(.name|test("checkout";"i"))|((.completedAt|fromdate)-(.startedAt|fromdate))]|add'

병합이 열 배가 되면 머지 큐가 먼저 넘친다

사람 열 명이 하루 30건을 병합하던 저장소에 에이전트를 붙여 300건이 되면, 머지 큐가 먼저 넘칩니다. 계산에 쓴 조건은 셋입니다.

  • CI는 20분이고 잡 8개가 평균 10분씩 돕니다.
  • PR 하나는 병합까지 푸시 두 번, 머지 큐 한 번으로 CI를 세 번 돕니다.
  • 러너는 Team 요금제의 60개입니다.
하루 30건42%한도 72건. CI 러너 8%
하루 300건417%나머지는 다음 날로. CI 러너 83%
동시 빌드 583%한도 360건. CI 러너도 83%
하루 병합 수에 따른 머지 큐의 한도 대비 사용률

머지 큐는 동시 빌드 수를 올려 넓힙니다. 동시 빌드는 앞 PR들을 미리 합친 상태로 함께 검사합니다. 앞의 PR 하나가 실패하면 뒤의 빌드가 모두 다시 돌아서, 테스트가 자주 깨지는 저장소는 늘린 만큼 빨라지지 않습니다.

그다음은 CI 러너가 막힙니다. 이 팀은 하루 360건쯤에서 러너를 다 씁니다. 하루 내내 고르게 돌 때 이야기라, 업무 시간에 몰리면 더 일찍 막힙니다.

머지 큐를 처음 켠다면

동시 빌드 수는 브랜치 규칙의 머지 큐 설정에서 Build concurrency로 1~100 사이를 고릅니다. GitHub Actions 워크플로에 merge_group 트리거가 없으면 큐의 검사가 필수 체크를 보고하지 못해 병합이 멈춥니다. pull_request와 함께 적어 둡니다.

yaml
on:
pull_request:
merge_group:

git은 가장 늦게 막힌다

하루 300건에서도 체크아웃이 한도의 0.14%에 그치는 것은 얕은 클론 덕분입니다. 얕은 클론은 최근 커밋 몇 개만 받습니다. actions/checkout은 fetch-depth가 기본 1이라 따로 정하지 않으면 얕게 받습니다. fetch-depth: 0으로 전체 이력을 받는 잡이 있다면 그 잡만 고치면 됩니다.

2.6GB인 vercel/next.js는 최근 커밋 25개만 받아서, CI 실행 하나의 체크아웃 단계가 잡 시간 합의 2%입니다.

Anthropic은 git 체크아웃에서 막혔습니다. 여러 서비스를 한 저장소에 둔 모노레포의 하루 병합이 5월 약 2,500건에서 9월 3만 건 넘게 늘었습니다.

작업 사본은 압축해도 25GB가 넘고, CI만 하루 약 2,500만 번 체크아웃합니다. 그대로 받으면 하루 600PB라, 얕은 클론과 사내 git 미러 수백 대로 버티다가 자체 버전 관리 Scion으로 옮겼습니다.

  • 체크아웃미러에서 몇 분필요한 파일만 몇 초
  • diff·blame잡마다 받은 사본에서서버에 묻기
  • 머지 큐저장소 전역 락병합 뒤·배포 전 테스트
git으로 버틸 때와 Scion으로 옮긴 뒤

Anthropic은 그 규모에서 머지 큐의 저장소 전역 락을 유지할 수 없었습니다. 대신 병합 뒤와 배포 전에 자동 테스트를 돌리고, 동시에 들어온 변경이 부딪히면 Claude가 고칩니다.

Scion은 외부에 내놓지 않습니다. 코드 리뷰·검색·CI까지 따로 갖춰야 해서 다른 조직은 옮기기 어렵다고 설명합니다. 서버가 diff와 blame에 답하는 방식만큼은 git에도 옮길 수 있다고 보고, GitHub·GitLab과 함께 풀겠다고 밝혔습니다.

Comments

댓글

powered by giscus ↗