롤백 제어(Revert Control)가 패치에 플레이크 동결을 적용할지 결정합니다
요약
이 글은 에이전트 패치 테스트의 간헐성(intermittency) 문제를 다루며, 롤백 제어(Revert Control)를 통해 소유권 기반의 안정적인 테스트 환경 구축을 강조합니다. 특히 '증인 번들'과 깨끗한 두 번째 러너 사용의 중요성을 설명하며, 체계적인 실행 및 셀 분류 방법을 제시합니다.
핵심 포인트
- 롤백 제어는 패치에 플레이크 동결을 적용할지 결정하는 핵심 메커니즘입니다.
- 테스트 환경은 단순히 트레이스백이 아닌 '증인 번들'과 같은 닫힌 기록으로 관리되어야 합니다.
- 간헐적 실패를 방지하기 위해 깨끗한 두 번째 러너(clean second runner) 사용이 필수적입니다.
- 셀의 상태는 모든 k번의 실행 결과를 종합하여 STABLE_PASS, STABLE_FAIL 등으로 분류해야 합니다.
패치 범위의 flake freeze는 에이전트 패치가 간헐성(intermittency)을 소유하고 있을 때만 유효합니다. 롤백된 트리가 동일한 증인(witness)에서 혼합되어 있다면, 이 동결은 패치가 아닌 하네스에 속해야 합니다. 두 번째 녹색 실행이 이러한 소유권 문제를 해결해주지는 않습니다.
에이전트 패치는 CI 로그에서 동일하게 보이는 이유로 속성 검사(property checks)에 실패합니다. 안정적인 로직 누락, 더러운 로컬 피처(dirty local fixture), 하네스 레이스 등 모든 것이 하나의 빨간 줄을 출력합니다. 모든 빨간 줄을 무시하면 회귀(regressions)를 숨기고, 아무것도 무시하지 않으면 노이즈로 인해 병합(merges)이 차단됩니다. 소유권은 측정되어야 하며, 이것이 바로 롤백 제어의 목적입니다.
먼저 증인 번들(witness bundle)을 닫으세요
증인 번들은 닫힌 기록입니다. 단순히 붙여넣기 한 트레이스백이 아닙니다. 이 글에서 나중에 설명할 검사기는 어떤 동결 정책도 적용하기 전에 불완전한 기록은 거부합니다. 목록을 로컬에서 실행할 수 있는 참조 구현으로 취급하세요. 본 기사는 코퍼스 통과율, flake 빈도 또는 프로덕션 인시던트 수를 보고하지 않습니다.
분류가 시작되기 전에 다음 필드를 요구합니다:
witness_id: 속성 이름, 시드(seed), 그리고 실패한 원본 입력 바이트의 해시 값.fixture_digest: 설정 후 속성이 실행되기 전 피처 바이트의 다이제스트. 파일 이름이 변경되어 충돌이 발생하지 않도록 경로를 해시된 목록에 포함합니다.patch_id: 재실행할 에이전트 디프(diff)의 체크섬.revert_id: 부모 트리의 트리 ID, 또는 에이전트 헝크(hunks)가 제거된 해당 디프의 트리 ID.runs: 계획된 반복마다 하나의 불리언 값.runner_id와tree(patch또는revert)로 태그 지정됩니다.k: 첫 번째 재실행 전에 선택되는 셀당 반복 횟수. 두 개의 러너와 두 개의 트리가 있는 경우, 실행 목록의 길이는4 * k여야 합니다.
러너 A는 잠긴 로컬 작업 트리입니다. 러너 B는 다른 머신이나 다른 컨테이너에서 깨끗하게 체크아웃된 상태입니다. 공유 캐시, 에디터 스왑 파일, 남은 서비스들은 다이제스트 문자열이 일치하더라도 러너 A를 부적격으로 만듭니다.
모델 투표가 아닌 깨끗한 두 번째 러너를 사용하세요
공개: 본 기사는 MonkeyCode의 제품 아웃리치(product outreach)의 일부로 작성되었습니다.
MonkeyCode의 무료 서버 옵션은 팀에 여분의 깨끗한(clean) 기계가 없을 때 runner B를 호스팅하는 한 가지 방법입니다. 무료 모델 접근 방식은 실패한 입력으로부터 후보 속성 문장(candidate property sentence)을 초안 작성할 수 있습니다. 이 문장은 초안일 뿐입니다. 사람이 이를 실행 가능한 검사로 변환하고 코드를 커밋해야만 유효합니다. 모델 텍스트는 아래 매트릭스의 셀에 절대 차지하지 않습니다. 본 기사는 단지 이 두 가지 가용성 사실만을 진술합니다. 모델, 할당량(quota), 하드웨어, 지속 시간 또는 영구성에 대해서는 언급하지 않습니다.
제품 이름을 제거해도 계약 내용은 변함이 없습니다. 깨끗한 두 번째 runner와 인간이 소유한 실행 가능한 속성이 있으면 충분합니다.
셀 분류 후 트리 비교
계획된 모든 실행을 완료하세요. 셀이 녹색으로 바뀌었다고 멈추지 마세요.
- 모든
k번의 실행이 통과했을 때 해당 셀을STABLE_PASS로 표시합니다. - 모든
k번의 실행이 실패했을 때 해당 셀을STABLE_FAIL로 표시합니다. - 최소한 한 번은 실행이 통과하고 한 번은 실패했을 때 해당 셀을
MIXED로 표시합니다. - 두 runner 모두 동일한 레이블을 할당할 때만 트리 클래스를 수락합니다.
- runner들이 의견이 다르거나, fixture digest가 다르면 환경 편향(environment skew)으로 중단합니다. 부울 값들을 평균하여 통과율을 내지 마세요.
하나의 패턴은 패치 범위 동결(patch-scoped freeze)을 허용합니다. 두 runner 모두 패치 셀을 MIXED로 표시해야 하며, 둘 다 revert 셀을 STABLE_PASS로 표시해야 합니다. 부모 속성이 유지됩니다. agent patch는 간헐적(intermittent)이 된 첫 번째 트리입니다.
다른 모든 합의 패턴은 그러한 동결을 거부합니다:
- Revert가 두 runner 모두에서
MIXED인 경우. 플래크(flake)가 이미 부모 트리에 있으므로, 대상은 하네스(harness) 또는 fixture입니다.patch_id에 첨부된 면제(waiver)는 이를 숨길 것입니다. - Revert가
STABLE_FAIL인 경우. 부모가 이미 이 증거를 놓치고 있습니다. agent 변경이 당신이 면제하려는 모드를 생성하지 않았습니다. - Patch가
STABLE_FAIL인 경우. 검사를 닫힌 상태로 유지하세요. 이것은 간헐적 레이블이 아니라 누락(miss)입니다. - Patch가
STABLE_PASS인 경우. 동결할 실패가 없습니다.
참조 확인기 (Reference checker)
이 내용을 witness_freeze.py로 저장하세요. 검토를 위한 제안 코드입니다. 데모를 실행하면 구성된 네 개의 행렬에 대한 결정 이름이 출력됩니다. 이 출력은 측정값이 아닙니다.
from __future__ import annotations
from collections import defaultdict
...
기록된 스위트 실행이 아닌, 제안되는 검사입니다:
def test_digest_mismatch_is_skew():
runs = []
for runner in ("runner-a", "runner-b"):
...
python witness_freeze.py로 데모를 실행하세요. 예상되는 줄은 cases 딕셔너리에 있는 네 개의 결정 이름이며, 삽입 순서(insertion order)와 일치해야 합니다.
번호가 매겨진 워크플로우
먼저 k를 선택하여 번들 안에 작성하세요. 녹색 상태가 될 때까지 적응적으로 재실행하는 것은 표본이 아닙니다. k >= 2를 사용하세요. 더 큰 k는 러너 시간 비용을 발생시키고 희귀한 누락(rare miss)이 안정적으로 보이는 가능성을 줄입니다. 이 문서는 시간 예산을 설정하지 않습니다.
- 첫 번째 빨간색 속성 검사에서 시드, 원시 입력 및 피처(fixture) 다이제스트를 캡처합니다. 그 이후에는 피처를 수정하지 마세요. 나중에 발견된 축약된 입력으로
witness_id를 대체하지 마세요. - 에이전트 패치가 최신 커밋인 경우,
patch_id를 위해git show --binary --format= HEAD를 해시하고,revert_id는git rev-parse 'HEAD^^{tree}'에서 설정합니다. 만약 패치가 아직 커밋되지 않은 상태라면, 대신git diff --binary HEAD를 해시하고 리버트 트리는git rev-parse 'HEAD^{tree}'를 사용하세요. - 러너 A에서 속성(property)을 패치 트리에서
k번, 그리고 리버트 트리에서k번 실행합니다. 부울 값을 저장하세요. 로그는 첨부 파일로 보관하세요. 로그 텍스트를 분류기(classifier)에 입력하지 마세요. - 깨끗하게 체크아웃된 상태에서 러너 B에서 3단계를 반복합니다. 재사용되는 볼륨이나 복사된
.pytest_cache가 없어야 합니다. 무료 서버 좌석은 선택 사항입니다. 격리(Isolation)가 요구사항입니다. - 어떤 셀이든
k번 미만의 실행을 하거나, 피처 다이제스트가 다르거나, 알 수 없는 러너 ID가 나타나면 번들을 거부하세요. classify를 호출합니다. 오직PATCH_FREEZE_CANDIDATE에 대해서만 패치 범위의 동결(freeze)을 작성하세요. 나중에 감사를 할 수 있도록 다른 모든 열거형 값(enum value)은 거부로 영구 저장하세요.
On HARNESS_NOT_PATCH의 경우, 픽스처(fixture) 또는 속성 하네스(property harness)에 대한 티켓을 열어주세요. 에이전트 패치 ID에 웨이버(waiver)를 걸지 마십시오.
ID를 바이트와 연결 상태로 유지하는 명령어들:
git show --binary --format= HEAD | sha256sum | awk '{print $1}'
git rev-parse 'HEAD^^{tree}'
find fixtures -type f -print0 | sort -z | xargs -0 sha256sum | sha256sum
세 번째 명령어는 경로 목록과 콘텐츠 해시를 결합하여 해싱합니다. 이 방식은 빈 디렉토리와 파일 모드를 누락합니다. 만약 모드 비트가 속성에 영향을 미친다면, 외부 해시 전에 find -printf로 모드 데이터를 목록에 추가하세요. 다이프(diff)의 서술적 요약이 아닌, 재생하는 바이트를 해싱해야 합니다.
디자인 제약 조건으로 매트릭스 읽기
세 개의 레이블을 가진 두 개의 트리가 있고, 러너들이 동의해야 하는 요구사항: 총 아홉 쌍입니다. 오직 한 쌍만이 패치 범위 고정(patch-scoped freeze) 대상입니다. 만약 러너들이 의견이 다를 수 있다면, 각 트리는 아홉 개의 러너-쌍 상태를 가지며 그 곱은 81이 됩니다. 이러한 개수는 정책을 설명하는 것이지, 에이전트 패치에 대한 필드 비율(field rate)은 아닙니다.
| Patch class, runners agree | Revert class, runners agree | Decision |
|---|---|---|
| MIXED | STABLE_PASS | Patch-scoped freeze candidate |
| ... | ||
Disagreement는 해당 행을 ENVIRONMENT_SKEW로 축소시킵니다. 러너들의 평균값은 러너 B가 추가되어 포착하려 했던 격리 실패(isolation failure)를 숨길 수 있습니다. |
거부 사항을 고정(freeze)과 동일한 형태로 저장하기
패치 옆에 JSON 기록을 유지하세요. 테스트 파일 내의 주석은 기록이 아닙니다. 거부 사항과 단일 후보 결정 모두에 대해 동일한 스키마를 사용해야, 빨간색 로그 라인의 삭제가 조용한 통과(quiet pass)가 아니라 누락된 파일로 보이게 할 수 있습니다.
{
"decision": "HARNESS_NOT_PATCH",
"witness_id": "9c1e",
...
위 식별자들은 스키마를 위한 자리 표시자입니다. 이를 사용자의 명령어에서 얻은 다이제스트(digest)로 대체하세요. 실제 고정 파일에 복사하지 마십시오.
제한 사항
롤백 제어는 제거할 수 있는 다이프가 필요합니다. 포맷팅, 락파일 변경(lockfile churn), 로직을 혼합한 압축된 블롭(squashed blob)은 의미 있는 롤백 셀을 갖지 못합니다. 해당 변경 사항을 분리하거나 고정을 거부해야 합니다.
공유된 다운스트림 상태(downstream state)가 두 러너(runner)를 모두 혼란스럽게 합니다. 하나의 스테이징 API(staging API)를 호출하는 두 개의 깨끗한 체크아웃(clean checkout)은 API 자체가 섞여 있기 때문에 둘 다 MIXED로 판정될 수 있습니다. 이 경우 검사기(checker)가 하네스(harness)에 책임을 돌릴 것입니다. 그게 맞을 수도 있고, 하네스가 리포지토리 밖에 있을 수도 있습니다. 티켓을 제출하기 전에 종속성을 확인하세요.
k = 2는 여전히 드문 누락(miss)을 우연히 안정적이라고 판정할 수 있습니다. k 값은 실제로 보유한 러너의 범위 내에서만 높이세요. 실행하지 않을 추가 반복(repeat)을 만들지 마십시오.
두 트리에 걸쳐 유지되는 속성이라도 여전히 약한 속성일 수 있습니다. 이 게이트는 단언(assertion) 강도를 순위 매기지 않습니다. 만약 당신이 신경 쓰는 위험이 전제 조건(predicate)이 무엇을 제외하는지에 대한 별도의 검토와 관련된다면, 이를 함께 사용하십시오.
fixtures 디렉토리 아래 파일에 타임스탬프를 기록하는 설정 과정(setup) 때문에 피처 목록 해시가 표류합니다. 해싱하기 전에 해당 경로들을 표준화하거나 제외하고, 그렇지 않으면 모든 실행이 환경 편향(environment skew)이 되어 매트릭스가 닫히지 않습니다.
누가 이것을 건너뛰어야 하는가
리버트 트리(revert tree)를 빌드할 수 없거나, 러너 B가 시도 사이에 깨끗하게 정리될 수 없거나, 논의가 실행 가능한 속성보다는 탐색적 세션에 관한 것이라면 이 워크플로우를 건너뛰세요. 또한 팀이 부울 매트릭스(boolean matrix)를 대체하는 모델 단락을 받아들일 경우에도 건너뛰어야 합니다. 매트릭스가 결정입니다. 단락은 선택적인 초안 작성입니다.
만약 무료 서버 좌석 자체가 깨끗한 두 번째 체크아웃을 얻는 방법이라면, 그 좌석을 러너 B에 바인딩하고 classify를 리포지토리에 남겨두세요. 동결 파일(freeze file)은 벤더 이름이 아니라 witness_id와 결정 열거형(decision enum)을 인용해야 합니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기