GitHub 차세대 Git 스토리지 해부: 에이전트 동시 푸시를 지원하는 Per-Ref 쿼럼과 불변 객체 분리 아키텍처
수천 개의 AI 에이전트 동시 쓰기를 수용하기 위해 GitHub이 전환한 차세대 Git 스토리지 아키텍처를 정리한다. 저장소 단위 전역 잠금에서 참조(Ref) 단위 쿼럼으로의 축소, 불변 객체와 메타데이터 분리, 수평 압축 샤딩의 원리를 살펴보고 Python 동시성 시뮬레이션 6개 검사를 확인한다.
글자가 전혀 없는 순수 추상적·기하학적 아트워크. Git의 분산 노드, 불변 블록, 다중 에이전트의 병렬 레일과 Per-Ref 합의 궤도를 표현한 그래픽 조형이며 실제 제품 화면은 아니다.
자율 AI 코딩 에이전트와 고빈도 CI/CD 파이프라인이 수천 단위로 늘어나면서 Git 인프라는 전례 없는 병목을 맞닥뜨렸다. 기존 Git 고가용성 클러스터는 저장소 전체에 걸리는 전역 잠금(Global Lock)이나 리포지토리 레벨 쿼럼을 전제로 설계되어, 아무리 많은 컴퓨팅 자원을 투입해도 단일 저장소에는 오직 하나의 쓰기(Push)만 직렬화되어 처리될 수 있었다. 여기에 클라우드 리전 간 통신 지연(RTT)이 더해지면 다수의 에이전트가 서로 다른 브랜치에 코드를 밀어 넣더라도 대기열이 기하급수적으로 늘어나며 푸시 타임아웃 장애로 이어진다.
GitHub이 2026년 11월 엔지니어링 블로그를 통해 공개한 차세대 Git 스토리지 아키텍처는 이 병목을 풀기 위해 두 가지 대원칙을 제시한다. 합의의 범위를 시스템 전역에서 참조(Ref) 단위로 축소하는 '최소 조정(Minimize Coordination)'과, 컴퓨트 노드와 스토리지 티어를 완전히 분리하는 '스토리지-컴퓨트 디커플링(Decoupling Storage from Compute)'이다. 불변 객체(Blobs, Trees, Commits)는 오브젝트 스토리지에 무경합 병렬로 기록하고, 브랜치 갱신만 Per-Ref 쿼럼으로 원자적 처리하며, 무거운 가비지 컬렉션은 백그라운드 수평 압축 샤딩(Horizontal Compaction Sharding)으로 분리해 쓰기 처리량을 최대 35배까지 끌어올렸다. GitHub 차세대 Git 인프라 발표
이 글은 GitHub 공식 엔지니어링 기술 문서를 바탕으로 에이전트 스케일 쓰기 병목의 원인과 해결 아키텍처를 정리하고, 로컬 Python 동시성 시뮬레이션으로 6가지 핵심 제약을 검증한다. Azure Blob Storage의 실제 클라우드 API 호출이나 GitHub 프로덕션 클러스터 자체를 시험한 결과는 아니다.
1. 전역 리포지토리 락이 에이전트 환경에서 무너지는 이유
전통적인 Git 서버 인프라는 파일시스템의 .git 디렉터리를 직접 다루는 일체형 서버에 의존했다. 고가용성(HA) 복제 환경에서도 레포지토리 복제본들 사이에서 데이터 일관성을 지키기 위해 저장소 전체를 하나의 잠금 단위로 묶었다.
- 브랜치 무관 직렬화: 에이전트 A가
feature-login에 푸시하고 에이전트 B가feature-payments에 푸시하더라도, 저장소 전역 락 때문에 A의 푸시가 완전히 끝날 때까지 B는 대기해야 한다.
- 리전 간 RTT 누적: 고가용성을 위해 지리적으로 분산된 복제 노드들이 동기식 합의를 수행할 때 왕복 지연시간(수십 ms)이 발생하며, 쓰기가 대기열에 쌓일수록 지연은 선형적으로 폭증한다.
- GC 및 팩파일 I/O 간섭: 수많은 에이전트가 쏟아내는 작은 커밋들은 다수의 느슨한 객체(Loose Objects)와 소형 팩파일을 생성한다. 기존 구조에서는 이를 최적화하기 위한 가비지 컬렉션(
git gc)이나 재압축이 실행되는 동안 메인 푸시 경로의 I/O가 심각하게 잠식된다.
에이전트가 늘어날수록 쓰기 대기 시간은 기하급수적으로 길어지며, 결국 RPC failed; HTTP 504 curl 22 The requested URL returned error: 504와 같은 푸시 실패가 일상이 된다.
2. 최소 조정과 불변 객체 분리 저장
차세대 Git 스토리지의 첫 번째 전환점은 Git의 내부 데이터 모델이 가진 본질적인 특성을 활용한 것이다.
Git의 핵심 객체인 블롭(Blob), 트리(Tree), 커밋(Commit)은 내용의 암호화 해시(SHA-1 / SHA-256)로 식별되는 불변(Immutable) 콘텐츠 주소화(Content-Addressable) 데이터다. 즉, 동일한 커밋이나 파일은 시스템 내 어디에 기록되든 고유하며, 한 번 기록되면 내용이 변경되지 않는다. 따라서 객체 생성 자체는 전역적인 순서 합의나 락을 필요로 하지 않는다.
레거시 저장소 단위 전역 락과 차세대 Per-Ref 쿼럼 및 스토리지 분리 구조의 비교 다이어그램.
GitHub은 이 특성을 바탕으로 컴퓨트 티어와 영구 저장소 티어(Azure Blob Storage 기반의 가상 복제본)를 분리했다.
| 구분 | 레거시 아키텍처 (Global Lock) | 차세대 아키텍처 (Next-Gen Storage) |
| 잠금 범위 | 저장소 전체 (Repository-wide) | 개별 참조 단위 (Per-Reference) |
| 객체 저장 방식 | 로컬 디스크 일체형 결합 기록 | 오브젝트 스토리지(Blob) 직접 병렬 기록 |
| 동시 푸시 동작 | 서로 다른 브랜치도 완전 직렬화 (동시성 1) | 서로 다른 브랜치는 100% 무경합 병렬 처리 |
| 충돌 감지 범위 | 저장소 단위 경합 및 타임아웃 | 동일 브랜치 원자적 CAS 불일치 시만 발생 |
| 가비지 컬렉션(GC) | 메인 스토리지 서버에서 인라인 실행 | 독립 워커가 샤드 리스(Lease) 기반 수평 처리 |
| 읽기 확장성 | 쓰기 노드의 로컬 복제본에 종속 | 무상태 컴퓨트 노드가 오브젝트 스토리지에서 탄력 확장 |
새로운 아키텍처에서는 에이전트가 푸시한 수천 개의 Git 객체를 오브젝트 스토리지에 락 없이 병렬로 쏟아붓는다. 이때 시스템 전반의 병목은 사라지고, 오직 '어떤 브랜치가 최종적으로 어떤 커밋 해시를 가리키는가'를 결정하는 참조 갱신 단계만 남게 된다.
3. 참조 단위 쿼럼(Per-Ref Quorum)과 원자적 CAS
전역 합의가 축소된 자리에는 **참조별 쿼럼(Per-Reference Quorum)**이 들어섰다.
기존 시스템이 "이 레포지토리에 푸시할 권한이 누구에게 있는가"를 물었다면, 차세대 시스템은 "refs/heads/feature-123의 최신 커밋이 commit-A에서 commit-B로 안전하게 전진(Fast-Forward)하는가"만 묻는다.
- 상호 독립적인 병렬 쓰기: 수백 개의 에이전트가 각자 독립된 기능 브랜치에 푸시할 때, 각 브랜치는 완전히 분리된 쿼럼 레지스트리에서 갱신된다. 에이전트 상호 간의 지연 간섭은 0에 수렴한다.
- 원자적 CAS (Compare-And-Swap): 동일한 브랜치에 두 에이전트가 동시에 푸시하는 경우에만 원자적 CAS 검사가 발동한다. 먼저 도착한 커밋이 브랜치 포인터를 전진시키면, 늦게 도착한 푸시는
expected old commit불일치로 즉시 거부(Non-fast-forward rejection)되어 데이터 덮어쓰기를 방지한다.
- 분산 롤백 안전성: 만약 객체가 오브젝트 스토리지에 기록된 후 참조 갱신 단계에서 CAS 충돌이나 네트워크 실패가 발생하더라도, 브랜치 포인터는 안전하게 이전 상태를 유지한다. 업로드된 고아(Dangling) 객체는 이후 백그라운드 청소 작업의 대상이 될 뿐, 저장소의 정합성을 훼손하지 않는다.
4. 수평 압축 샤딩(Horizontal Compaction Sharding)
에이전트가 쏟아내는 고빈도 커밋들은 무수히 많은 소형 팩파일(Packfile)을 만든다. 이를 방치하면 읽기 시 수많은 팩 인덱스를 탐색해야 하므로 읽기 성능이 급격히 저하된다.
GitHub은 이를 해결하기 위해 수평 압축 샤딩(Horizontal Compaction) 메커니즘을 도입했다:
- 오픈 세그먼트와 봉인(Seal): 새로 들어오는 팩 객체들은 활성 오픈 세그먼트에 빠르게 추가된다. 세그먼트가 일정 크기나 시간 임계값에 도달하면 즉시 '봉인(Sealed)' 상태로 전환되고 새로운 세그먼트가 열린다.
- 분산 리스(Lease) 기반 워커: 별도의 무상태 압축 워커 노드들이 저장소의 특정 팩 샤드에 대해 분산 리스를 획득한다.
- 비간섭 최적화 병합: 워커는 봉인된 세그먼트들을 가져와 객체 무결성을 검증하고, 중복을 제거하며, 최적화된 대형 델타 팩파일로 병합한 뒤 저장소 매니페스트를 갱신한다. 이 모든 과정은 메인 쓰기/푸시 API 경로와 완전히 격리되어 수행된다.
5. Python 동시성 시뮬레이션으로 확인한 6가지 동작
이 차세대 Git 스토리지의 6대 핵심 설계 원리를 재현하고 검증하기 위해 Python 3.12 환경에서 멀티스레드 동시성 시뮬레이션 스크립트를 작성했다.
로컬 실행을 통해 다음 6가지 핵심 시나리오를 검증했다:
- 불변 객체 CAS 무경합 병렬 쓰기: 5개 스레드에서 동시에 SHA-256 객체를 생성하여 데이터 충돌 없이 고유 해시 기반으로 100% 저장됨을 확인했다.
- 전통적 전역 락 방식의 강제 직렬화 재현: 구 아키텍처 모델에서 4개 브랜치에 동시 푸시를 시도했을 때, 리포지토리 전역 락으로 인해 최대 동시 실행자 수(
max_concurrent_writers)가 1로 고정되며 총 작업 시간이 선형 누적됨을 확인했다.
- Per-Ref 세분화 락으로 동시 병렬 푸시 확인: 차세대 모델에서는 4개의 서로 다른 브랜치 푸시가 동시에 진행되어 최대 동시 실행자가 4까지 확장되었고, 경합 없이 모두 즉시 완료되었다.
- 동일 Ref 동시 갱신 시 원자적 CAS 실패 보호: 동일한
main브랜치에 대해 동일한 부모 커밋을 기반으로 두 에이전트가 동시 푸시를 시도했을 때, 1건만 정상 반영되고 나머지 1건은 원자적 CAS 실패(Non-fast-forward 거부)로 정확히 차단되었다.
- 수평 압축 샤딩 및 세그먼트 봉인/병합 검증: 7개의 팩 객체가 유입될 때 임계값(3개)에 도달한 세그먼트 2개가 자동으로 봉인되고, 독립 워커가 6개의 객체를 무결성 검증과 함께 단일 최적화 팩으로 성공적으로 병합함을 확인했다.
- 트랜잭션 실패 시 Ref 무결성 보존 및 댕글링 객체 격리: 잘못된 기준 커밋으로 푸시가 거부되었을 때, 오브젝트 스토리지에는 불변 데이터가 격리 보존된 채 기존 브랜치 참조는 훼손되지 않고 원본 커밋을 유지함을 확인했다.
모든 6개 테스트는 All 6 verification checks passed successfully!로 통과했다.
6. 대규모 에이전트 도입을 위한 엔지니어링 체크리스트
GitHub의 스토리지 인프라가 병렬 쓰기를 지원하더라도, 클라이언트 측 에이전트 파이프라인의 설계가 부실하면 소프트웨어 레벨의 충돌이 발생할 수 있다. 에이전트 기반 개발 환경을 운영할 때는 다음 4단계를 점검해야 한다.
- 에이전트별 독립 토픽 브랜치 격리: 에이전트가 공유 브랜치(예:
develop,staging)에 직접 푸시하지 않도록 설계한다.agent/{agent-id}/{task-id}형태의 고유 브랜치로 푸시하도록 강제하여 Per-Ref 쿼럼의 병렬성을 최대화한다.
- CAS 실패 시 지수 백오프와 자동 리베이스: 동일 브랜치 경쟁으로 푸시가 거부되었을 때 단순 무한 재시도를 반복하면 스토리지에 무의미한 댕글링 객체가 누적된다. 최신 원격 커밋을 페치하여 리베이스(Rebase)하고 지수 백오프(Exponential Backoff)를 거쳐 재시도한다.
- 읽기 복제본의 최종 일관성(Eventual Consistency) 고려: 스토리지와 컴퓨트가 분리된 환경에서는 푸시 직후 읽기 요청이 아직 동기화되지 않은 가상 복제본으로 라우팅될 수 있다. 에이전트가 자신이 방금 푸시한 커밋을 즉시 검증해야 한다면 쓰기 확인(Write Acknowledgment)을 거친 최신 Ref를 명시적으로 조회한다.
- 고빈도 소형 커밋 묶음(Squash) 최적화: 백그라운드 압축 샤딩이 동작하더라도 지나치게 잘게 쪼개진 수천 개의 마이크로 커밋은 네트워크 오버헤드를 유발한다. 에이전트의 내부 실험 단계에서는 로컬 스쿼시를 수행한 뒤 정리된 커밋 단위로 푸시한다.
참고 자료
확인 기준: November 8, 2026 공개 공식 기술 발표. 이 글에서는 로컬 Python 동시성 시뮬레이션을 통해 6가지 핵심 제약을 검증했으며, Azure Blob Storage의 실제 클라우드 인프라 통신 및 GitHub 프로덕션 클러스터 자체는 직접 시험하지 않았다.