하루 병합이 열 배로 늘면 팀 저장소는 어디부터 막히나
숫자 셋을 재면 어디가 먼저 막히는지 나온다
하루 병합 수, CI 한 번에 걸리는 시간, 실행 한 번의 잡 수를 잽니다. R에 저장소를, ci.yml에 기본 브랜치에서 도는 워크플로 파일 이름을 넣습니다. 잡 수는 Actions 탭에서 실행 하나를 열면 보입니다.
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에 넣고 체크아웃 단계 시간의 합(초)을 뽑아 잡 시간의 합과 견줍니다.
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개입니다.
머지 큐는 동시 빌드 수를 올려 넓힙니다. 동시 빌드는 앞 PR들을 미리 합친 상태로 함께 검사합니다. 앞의 PR 하나가 실패하면 뒤의 빌드가 모두 다시 돌아서, 테스트가 자주 깨지는 저장소는 늘린 만큼 빨라지지 않습니다.
그다음은 CI 러너가 막힙니다. 이 팀은 하루 360건쯤에서 러너를 다 씁니다. 하루 내내 고르게 돌 때 이야기라, 업무 시간에 몰리면 더 일찍 막힙니다.
머지 큐를 처음 켠다면
동시 빌드 수는 브랜치 규칙의 머지 큐 설정에서 Build concurrency로 1~100 사이를 고릅니다. GitHub Actions 워크플로에 merge_group 트리거가 없으면 큐의 검사가 필수 체크를 보고하지 못해 병합이 멈춥니다. pull_request와 함께 적어 둡니다.
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잡마다 받은 사본에서서버에 묻기
- 머지 큐저장소 전역 락병합 뒤·배포 전 테스트
Anthropic은 그 규모에서 머지 큐의 저장소 전역 락을 유지할 수 없었습니다. 대신 병합 뒤와 배포 전에 자동 테스트를 돌리고, 동시에 들어온 변경이 부딪히면 Claude가 고칩니다.
Scion은 외부에 내놓지 않습니다. 코드 리뷰·검색·CI까지 따로 갖춰야 해서 다른 조직은 옮기기 어렵다고 설명합니다. 서버가 diff와 blame에 답하는 방식만큼은 git에도 옮길 수 있다고 보고, GitHub·GitLab과 함께 풀겠다고 밝혔습니다.