Zuul의 Speculative Merge와 Cross-project Dependencies 살펴보기

Zuul의 Speculative Merge와 Cross-project Dependencies 정리본


0. 들어가며

최근 Zuul 프로젝트의 concepts.po 파일을 한글로 번역 하는 컨트리뷰션

작업을 진행하면서, '‘Speculative Merge(추측 기반 병합)’라는 용어를 접하게 되었습니다.

처음에는 단순히 “가상 병합” 정도로 이해했지만, 이 개념이 실제로 어떻게 동작하며 왜 대규모 협업 환경에서 중요한지 정확히 이해하고 싶어 추가로 학습하게 되었습니다.

특히 실습을 진행하는 과정에서

Zuul Hands-on Part 6: Cross-project Dependencies(교차 프로젝트 의존성) ( Zuul Hands on - part 6 - Cross project dependencies - Software Factory ) 문서를 참고하여

추측 기반 병합과 교차 프로젝트 의존성 기능을 직접 실험해 보았습니다.


1. 기존 CI/CD 도구(Jenkins)의 한계

전통적인 Jenkins 기반 CI는 주로 Post-merge(병합 후 검증) 방식으로 동작합니다.

즉, 코드가 main 브랜치에 병합된 이후 테스트가 수행됩니다.

문제 상황

  • 개발자 A와 B가 각각 테스트를 통과함
  • 두 패치가 함께 존재할 때 충돌이 발생함
  • main 브랜치가 **Broken State(빌드 오류 상태)**가 됨

결과

  • 빌드 복구 전까지 전체 팀 생산성 저하됨
  • 병합 이후 문제 발견 → 롤백 비용 증가함

2. Zuul의 추측 기반 병합

Zuul은 Pre-merge Gating 모델을 사용합니다.

변경 사항이 실제 병합되기 전에 모든 검증을 완료해야 합니다.

핵심 아이디어

  • 대기열(queue)에 있는 패치들이 이미 병합되었다고 가정함
  • 미래의 가상 상태를 시뮬레이션하여 테스트 수행함

동작 예시

  1. A 패치 테스트 수행
  2. B 패치 테스트 시 → A가 이미 병합되었다고 가정함
  3. C 패치 테스트 시 → A + B 병합 상태 가정함

내부적으로는 다음과 같은 가상 병합을 수행합니다:

git checkout main
git merge A
git merge B

이 방식이 바로 추측 기반 병합입니다.


3. Dependent(의존형) vs Independent(독립형) Pipeline

Independent Pipeline (독립형 파이프라인)

  • 각 변경 사항을 독립적인 워크 스페이스에서 테스트함
  • 다른 패치와 상호 영향을 주고 받지 않음

Dependent Pipeline (의존형 파이프라인)

  • 변경 사항을 큐에 묶어 순차적으로 추측 기반 병합함
  • 대규모 협업 환경에서 프로젝트 간 영향을 파악하기에 적합함

4. 교차 프로젝트 의존성

여러 프로젝트가 서로 의존하는 경우, 단일 프로젝트 테스트만으로는 안정성을 보장할 수 없습니다.

예를 들어, 라이브러리와 애플리케이션이 분리된 구조에서는 다음과 같은 문제가 발생할 수 있습니다.

  • 라이브러리 변경 사항은 테스트를 통과함
  • 애플리케이션도 기존 라이브러리 버전 기준으로는 정상 동작함
  • 그러나 두 변경 사항이 함께 존재하면 오류 발생

Zuul은 다음 두 가지 방식으로 이를 해결합니다:

  1. required-projects : 정적(구조적) 의존성
  2. Depends-On : 동적(변경 단위) 의존성

두 기능은 목적과 동작 범위가 다릅니다.


5. required-projects 설정 예제

이제 교차 프로젝트 테스트를 가능하게 만드는 실제 설정을 살펴보겠습니다.

.zuul.yaml

- job:
    name: tox-demorepo
    description: tox test for demo-repo with dependencies
    parent: tox-py27
    required-projects: # required-projects
      - demo-repo
      - demo-lib
    vars:
      zuul_work_dir: "{{ zuul.projects\['sftests.com/demo-repo'\].src_dir }}"

- project:
    check:
      jobs:
        - tox-demorepo
        - tox-pep8
    gate:
      jobs:
        - tox-demorepo
        - tox-pep8

설명

  • demo-libdemo-repo를 동일 워크스페이스에 체크아웃함
  • demo-lib 변경 사항이 demo-repo 테스트에 즉각 반영됨
  • 라이브러리 변경이 애플리케이션에 어떤 영향을 미치는지 병합 전에 확인함

즉, required-projects“어떤 프로젝트를 함께 테스트할 것인가” 를 정의합니다.


6. 실습 예제 구성: 왜 의존성 관리가 필요한가?

구조를 단순화한 예제를 통해 동작 방식을 살펴보겠습니다.

demo-lib (라이브러리)

# demo_lib/math_utils.py

def add(a, b):
    return a + b

demo-repo (애플리케이션)

# app.py

from demo_lib.math_utils import add

def calculate():
    return add(3, 4)

7. 실패 시나리오

그렇다면 required-projects 설정이 없다면 어떤 문제가 발생할까요?

demo-lib 변경

def add(a, b):
    return str(a + b)

이 경우 라이브러리 단독 테스트는 통과할 수 있습니다.

demo-repo 코드

그러나 demo-repo에서는:

result = add(3, 4)
print(result + 1) # 문자열과 숫자의 더하기 연산 발생

결과

TypeError가 발생함 

이 경우 다음과 같은 상황이 벌어집니다:

  • 라이브러리 테스트는 통과함
  • 애플리케이션 테스트도 (기존 라이브러리 기준으로는) 통과함
  • 하지만 실제 병합되어 함께 배포되면 장애가 발생함

required-projects가 있다면, 라이브러리 변경을 적용한 상태로 애플리케이션 테스트를 먼저 수행하여 병합 전에 문제를 차단합니다.


8. Depends-On 사용 예제

이번에는 라이브러리와 애플리케이션을 동시에 수정하는 상황입니다.

상황

  • demo-libmultiply 함수를 새로 추가함
  • demo-repo에서 해당 함수 사용하도록 수정함
  • 두 변경 모두 아직 main에 병합되지 않음

이때 단순히 프로젝트를 묶어주는 required-projects만으로는 부족합니다.

그 이유는 다음과 같습니다:

  • 라이브러리의 특정 패치
  • 애플리케이션의 특정 패치
  • 하나의 논리적 변경 세트로 묶어서 검증해야 하기 때문입니다.

demo-repo 커밋 메시지

Depends-On: https://review.example.com/c/demo-lib/+/12345

Zuul 동작

  1. demo-lib의 해당 패치(12345)를 추측 기반 병합함
  2. 그 위에 demo-repo 변경 사항 추측 기반 병합함
  3. 통합 테스트를 실행함

즉, Depends-On은 “이번 변경이 어떤 변경 위에서 동작해야 하는지”를 선언합니다.

정리

  • required-projects는 프로젝트 단위
  • Depends-On은 변경 사항 단위
  • 전자는 구조적 의존성
  • 후자는 일시적, 동적 의존성

한 문장으로 정리하면:

required-projects는 프로젝트를 묶고,

Depends-On은 변경 사항을 묶습니다.

9. 결론

추측 기반 병합은 단순한 가상 병합이 아닙니다.

  • 병합될 가능성이 있는 미래 상태를 먼저 검증함
  • 프로젝트 간 의존성을 사전에 확인함
  • 변경 사항 간의 논리적 관계까지 추적함

이를 통해 main 브랜치는 항상 배포 가능한 상태를 유지할 수 있습니다.

대규모 협업 환경, 특히 멀티 레포 구조에서는

required-projects로 구조를 묶고,

Depends-On으로 변경을 묶는 전략 이 필수적입니다.

핵심 키워드

  • Speculative Merge
  • Dependent Pipeline
  • Gate
  • required-projects
  • Depends-On