동시에 재고를 차감할 때 가끔 음수가 되지만 반복 테스트에서는 잘 재현되지 않습니다. 두 요청의 실행 순서를 어떻게 통제해 검증하나요?
잠깐, 답변을 보기 전에
먼저 내 말로 설명해 볼까요?
답변과 점검 포인트 펼치기답변 접기
구두 답변
정의를 외우기보다, 원리와 조건을 연결해 설명해 보세요.
반복 실행만으로 우연히 재현되기를 기다리기보다, 문제가 생기는 읽기·쓰기 사이의 순서를 의도적으로 만들겠습니다. 동시성 오류는 연산 자체보다 두 연산이 만나는 구간에 있는 경우가 많습니다. 어떤 불변식이 깨졌는지 먼저 적는 것이 출발점입니다.
배리어는 여러 실행 흐름이 정해진 지점에 모두 도착할 때까지 기다리게 하는 동기화 수단입니다. 설명용 재현 순서는 ‘A와 B가 같은 재고를 읽음 → 둘 다 읽은 지점에서 멈춤 → 두 쓰기를 허용함’입니다. 이 순서를 명시하면 우연한 스케줄링에 의존하지 않고 중복 차감 경로를 반복 확인할 수 있습니다.
두 요청이 같은 재고를 읽고 차감하는 버그라면 두 스레드가 읽기를 끝낸 지점에서 배리어로 기다리게 한 뒤 함께 쓰도록 하겠습니다. 타임아웃 테스트도 실제 시간을 길게 기다리기보다 주입한 시계와 제어 가능한 완료 신호를 사용할 수 있습니다. 임의의 sleep은 환경이 바뀌면 순서를 보장하지 못해 테스트가 불안정해지기 쉽습니다.
레이스 탐지기는 메모리 접근 오류를 찾는 데 도움이 되지만, 모두 원자적 연산인 코드의 잘못된 상태 변경 순서까지 전부 잡지는 못합니다. 저는 결과의 불변식과 금지된 상태 전이를 검증하고, 오류가 난 실행 순서와 랜덤 시드를 남기겠습니다. 결정적 테스트로 핵심 경로를 재현한 뒤 스트레스 테스트로 더 넓은 조합을 탐색하겠습니다. 테스트 통과를 모든 실행 순서의 증명으로 보지는 않되, 발견한 실패를 다시 놓치지 않는 회귀 테스트로 만드는 것이 중요합니다.
실패 이력을 직접 만듭니다
재고가 1일 때 A와 B가 모두 1을 읽고 각각 1을 차감하는 순서를 만들면 lost update를 재현할 수 있습니다. 두 실행 흐름을 읽기 완료 지점의 배리어에서 멈춘 뒤 쓰기를 차례로 허용하고, 최종 재고만이 아니라 조건부 UPDATE 영향 행 수와 차감 원장 수를 검사합니다. 둘 다 성공했는데 잔액이 0으로 보이는 오류를 최종 값 하나로는 놓칠 수 있습니다. 요청 ID·읽은 버전·쓰기 순서·난수 시드를 사건 로그로 남겨 같은 순서를 회귀 테스트로 고정합니다.
도구의 역할을 나눕니다
sleep은 스케줄러가 바뀌면 순서를 보장하지 않으므로 제어 가능한 신호나 가상 시계를 사용합니다. race detector·ThreadSanitizer는 보호되지 않은 메모리 접근에 유용하지만, 각 연산이 원자적이어도 ‘확인 후 차감’이라는 업무 전이가 잘못된 문제를 모두 찾지는 못합니다. 따라서 애플리케이션 락, DB 버전 조건, 고유 제약 중 실제 보호 지점을 확인하고, 결정적 시나리오 뒤에 다양한 지연과 취소를 탐색하는 스트레스 시험을 별도로 두겠습니다.
구체적인 경계 사례
취소와 완료 경쟁은 두 순서를 각각 시험합니다. 먼저 완료 신호를 게시한 뒤 취소하면 성공을 보존하는지, 취소가 실행권을 먼저 가져가면 늦은 완료가 새 상태를 덮지 않는지 검사합니다. 테스트용 훅은 보호해야 할 임계 구역 안에서 영원히 기다리지 않게 별도 기한을 두어 결함이 교착으로 바뀌어도 CI가 종료되게 합니다.
분산 저장소는 호출·응답 시각, 연산 ID, 입력·반환값을 이력으로 남기고 관찰된 결과를 설명하는 유효한 순차 순서가 존재하는지 검사할 수 있습니다. timeout 연산은 실행되지 않았다고 단정할 수 없으므로 불확정 연산으로 모델링합니다. 특정 스케줄을 재현하는 검사와 가능한 이력을 탐색하는 검사는 다른 범위이며, 둘 다 모든 장애의 완전한 증명을 뜻하지는 않습니다.
득점 포인트
이해가 드러나는 지점을 확인해 보세요.
- 배리어와 완료 신호로 읽기·계산·쓰기의 문제 순서를 재현한다.
- 최종 값뿐 아니라 버전·영향 행 수·원장과 불변식을 검사한다.
- race detector와 논리적 상태 경쟁, 결정적 회귀와 스트레스 탐색을 구분한다.
감점 포인트
놓치기 쉬운 조건과 오해를 짚어 보세요.
- sleep 반복을 실행 순서 보장의 수단으로 사용한다.
- 메모리 레이스 도구 통과를 업무 상태의 원자성 증명으로 본다.
- 최종 재고만 검사해 두 번 성공한 lost update를 놓친다.
더 파고들 거리
하나 더 질문하며 이해의 범위를 넓혀 보세요.
- 선형화 가능한 자료구조를 이력 기반으로 검증하려면 무엇을 기록할까요?
- 테스트를 위해 실패 지점을 넣으려 합니다. 운영 코드의 내부 구현을 과하게 노출하지 않고 무엇을 주입하나요?
- 분산 테스트에서 노드 종료·프로세스 정지·네트워크 분할은 어떤 서로 다른 실패를 재현하나요?
득점·감점 포인트는 학습용 자기 점검 기준이며, 특정 회사의 실제 채점표가 아닙니다.
원리부터 공부하고 싶다면
답변을 보충하는 개념·예제·슈도코드를 읽어 보세요.