Skip to content

Commit 04bda82

Browse files
authored
contribution by tlsntjr
1 parent 9e1c567 commit 04bda82

1 file changed

Lines changed: 272 additions & 0 deletions

File tree

Lines changed: 272 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,272 @@
1+
# Zuul의 추측 기반 병합(Speculative Merge)과 Cross-project 의존성(Dependencies) 정리본
2+
3+
------------------------------------------------------------------------
4+
5+
## 0. 들어가며
6+
7+
최근 **Zuul 프로젝트의 `concepts.po` 파일을 한글로 번역**하는 컨트리뷰션
8+
작업을 진행하면서, 'Speculative Merge(추측 기반 병합)'라는 용어를 접하게
9+
되었습니다.
10+
11+
처음에는 단순히 "가상 병합" 정도로 이해했지만, 이 개념이 실제로 어떻게
12+
동작하며 왜 대규모 협업 환경에서 중요한지 정확히 이해하고 싶어 추가로
13+
학습하게 되었습니다.
14+
15+
특히 실습을 진행하는 과정에서\
16+
**Zuul Hands-on Part 6: Cross-project Dependencies** (https://www.softwarefactory-project.io/zuul-hands-on-part-6-cross-project-dependencies.html) 문서를 참고하여\
17+
추측 기반 병합(Speculative Merge)와 Cross-project 의존성(Dependencies) 기능을 직접 실험해 보았습니다.
18+
19+
------------------------------------------------------------------------
20+
21+
## 1. 기존 CI/CD 도구(Jenkins)의 한계
22+
23+
전통적인 Jenkins 기반 CI는 주로 **Post-merge 방식**으로 동작합니다.
24+
25+
즉, 코드가 `main` 브랜치에 병합된 이후 테스트가 수행됩니다.
26+
27+
### 문제 상황
28+
29+
- 개발자 A와 B가 각각 테스트를 통과
30+
- 두 패치가 함께 존재할 때 충돌 발생
31+
- main 브랜치가 **Broken State** 상태가 됨
32+
33+
### 결과
34+
35+
- 빌드 복구 전까지 전체 팀 생산성 저하
36+
- 병합 이후 문제 발견 → 롤백 비용 증가
37+
38+
------------------------------------------------------------------------
39+
40+
## 2. Zuul의 Speculative Merge
41+
42+
Zuul은 **Pre-merge Gating 모델**을 사용합니다.
43+
44+
> 변경 사항이 실제 병합되기 전에 모든 검증을 완료합니다.
45+
46+
### 핵심 아이디어
47+
48+
- 대기열(queue)에 있는 패치들이 이미 병합되었다고 가정
49+
- 미래의 가상 상태를 시뮬레이션하여 테스트 수행
50+
51+
### 동작 예시
52+
53+
1. A 패치 테스트
54+
2. B 패치 테스트 시 → A가 이미 병합되었다고 가정
55+
3. C 패치 테스트 시 → A + B 병합 상태 가정
56+
57+
내부적으로는 다음과 같은 가상 병합을 수행합니다:
58+
59+
``` bash
60+
git checkout main
61+
git merge A
62+
git merge B
63+
```
64+
65+
이 방식이 바로 **Speculative Merge**입니다.
66+
67+
------------------------------------------------------------------------
68+
69+
## 3. Dependent vs Independent Pipeline
70+
71+
### Independent Pipeline
72+
73+
- 각 변경 사항을 독립적인 workspace에서 테스트
74+
- 다른 패치와 상호 영향 없음
75+
76+
### Dependent Pipeline
77+
78+
- 변경 사항을 큐에 묶어 순차적으로 speculative merge 수행
79+
- 대규모 협업 환경에 적합
80+
81+
------------------------------------------------------------------------
82+
83+
## 4. Cross-project Dependencies
84+
85+
여러 프로젝트가 서로 의존하는 경우, 단일 프로젝트 테스트만으로는
86+
안정성을 보장할 수 없습니다.
87+
88+
예를 들어, 라이브러리와 애플리케이션이 분리된 구조에서는 다음과 같은 문제가 발생할 수 있습니다.
89+
* 라이브러리 변경은 테스트를 통과
90+
* 애플리케이션도 기존 버전 기준으로는 저앙
91+
* 그러나 두 변경이 함께 존재하면 오류 발생
92+
93+
Zuul은 다음 두 가지 방식으로 이를 해결합니다:
94+
95+
1. `required-projects` : 정적(구조적) 의존성
96+
2. `Depends-On` : 동적(변경 단위) 의존성
97+
구 기능은 목적과 동작 범위가 다릅니다.
98+
------------------------------------------------------------------------
99+
100+
## 5. required-projects 설정 예제
101+
이제 Cross-project 테스트를 가능하게 만드는 실제 설정을 살펴보겠습니다.
102+
103+
`.zuul.yaml`
104+
105+
``` yaml
106+
- job:
107+
name: tox-demorepo
108+
parent: tox-py39
109+
required-projects:
110+
- demo-lib
111+
- demo-repo
112+
```
113+
114+
### 설명
115+
116+
- demo-lib와 demo-repo를 동일 workspace에 체크아웃
117+
- demo-lib 변경이 demo-repo 테스트에 반영됨
118+
- 라이브러리 변경이 앱에 어떤 영향을 미치는지 병합 전에 확인
119+
120+
즉, required-projects는 “어떤 프로젝트를 함께 테스트할 것인가”를 정의합니다.
121+
122+
------------------------------------------------------------------------
123+
124+
## 6. 실습 예제 구성
125+
구조를 단순화한 예제를 통해 동작 방식을 살펴보겠습니다
126+
127+
### demo-lib
128+
129+
``` python
130+
# demo_lib/math_utils.py
131+
132+
def add(a, b):
133+
return a + b
134+
```
135+
136+
### demo-repo
137+
138+
``` python
139+
# app.py
140+
141+
from demo_lib.math_utils import add
142+
143+
def calculate():
144+
return add(3, 4)
145+
```
146+
147+
### 기본 Zuul 설정
148+
149+
``` yaml
150+
- project:
151+
check:
152+
jobs:
153+
- tox-py39
154+
gate:
155+
jobs:
156+
- tox-py39
157+
```
158+
159+
------------------------------------------------------------------------
160+
161+
## 7. 실패 시나리오
162+
그렇다면 required-projects 설정이 없으면 어떤 문제가 발생할까요?
163+
164+
### demo-lib 변경
165+
166+
``` python
167+
def add(a, b):
168+
return str(a + b)
169+
```
170+
라이브러리 단독 테스트는 통과할 수 있습니다.
171+
172+
### demo-repo 코드
173+
그러나 demo-repo에서는:
174+
175+
``` python
176+
result = add(3, 4)
177+
print(result + 1)
178+
```
179+
180+
### 결과
181+
182+
TypeError 발생
183+
184+
이 경우:
185+
186+
* 라이브러리는 통과
187+
188+
* 애플리케이션도 기존 기준으로는 통과
189+
190+
* 하지만 함께 배포되면 장애 발생
191+
192+
required-projects가 있다면,
193+
194+
라이브러리 변경을 적용한 상태로 애플리케이션 테스트를 먼저 수행
195+
196+
하여 병합 전에 문제를 차단합니다.
197+
198+
------------------------------------------------------------------------
199+
200+
## 8. Depends-On 사용 예제
201+
한 단계 더 나아가 보겠습니다.
202+
203+
이번에는 라이브러리와 애플리케이션을 동시에 수정하는 상황입니다.
204+
205+
### 상황
206+
207+
- demo-lib에 `multiply` 함수 추가
208+
- demo-repo에서 해당 함수 사용
209+
- 두 변경 모두 아직 merge되지 않음
210+
211+
이때 단순히 require-projects만으로는 부족할 수 있습니다.
212+
213+
왜냐하면:
214+
- 라이브러리의 특정 변경과
215+
- 애플리케이션의 특정 변경을
216+
- 하나의 논리적 변경 세트로 묶어야 하기 때문입니다.
217+
### demo-repo 커밋 메시지
218+
219+
Depends-On: https://review.example.com/c/demo-lib/+/12345
220+
221+
### Zuul 동작
222+
223+
1. demo-lib의 해당 변경 사항 추측 기반 병합
224+
2. 그 위에 demo-repo 변경 사항 추측 기반 병합
225+
3. 통합 테스트 실행
226+
227+
즉, Depends-On은 “이번 변경이 어떤 변경 위에서 동작해야 하는지”를 선언합니다.
228+
229+
### 정리
230+
231+
- `required-projects`는 프로젝트 단위
232+
233+
- `Depends-On`은 변경 사항 단위
234+
235+
- 전자는 구조적 의존성
236+
237+
- 후자는 일시적, 동적 의존성
238+
239+
한 문장으로 정리하면:
240+
241+
required-projects는 프로젝트를 묶고,
242+
Depends-On은 변경 사항을 묶습니다.
243+
------------------------------------------------------------------------
244+
245+
## 9. 결론
246+
247+
추측 기반 병합(Speculative Merge)는 단순한 가상 병합이 아닙니다.
248+
249+
- 병합될 가능성이 있는 미래 상태를 먼저 검증하고
250+
251+
- 프로젝트 간 의존성을 사전에 확인하며
252+
253+
- 변경 간 관계까지 추적합니다.
254+
255+
이를 통해 `main` 브랜치는 항상 배포 가능한 상태를 유지합니다.
256+
257+
대규모 협업 환경, 특히 멀티 레포 구조에서는
258+
259+
- required-projects로 구조를 묶고
260+
261+
- Depends-On으로 변경을 묶는 전략이 필수적입니다.
262+
263+
------------------------------------------------------------------------
264+
265+
## 핵심 키워드
266+
267+
- Speculative Merge
268+
- Dependent Pipeline
269+
- Gate
270+
- required-projects
271+
- Depends-On
272+
- Cross-repo CI

0 commit comments

Comments
 (0)