Codex use case
장애 조사 및 수정 사항 테스트
장애 조사 자료를 바탕으로 버그를 재현하고, 필요한 부분만 수정한 뒤 검토용 인계 자료를 작성하세요.
Codex Cloud에 민감한 정보를 제거한 장애 조사 자료와 정상 작동하는 테스트 환경을 제공하세요. 증거를 조사해 코드 결함을 뒷받침하는 근거가 있으면 결함을 재현하세요. 필요한 부분만 수정한 패치와 함께 회귀 테스트 결과, 배포 및 롤백 인계 자료를 제출하세요.
Best for
- 장애 상황이 안정된 후 원인을 조사하는 소규모 엔지니어링 팀
- 민감한 정보를 제거한 조사 자료와 안전한 재현용 입력값이 있는 애플리케이션 결함
- 정상 작동하는 격리된 테스트 환경에서 특정 결함의 수정 사항을 검토하는 팀
Contents
장애 조사 및 수정 사항 테스트
장애 조사 자료를 바탕으로 버그를 재현하고, 필요한 부분만 수정한 뒤 검토용 인계 자료를 작성하세요.
Codex Cloud에 민감한 정보를 제거한 장애 조사 자료와 정상 작동하는 테스트 환경을 제공하세요. 증거를 조사해 코드 결함을 뒷받침하는 근거가 있으면 결함을 재현하세요. 필요한 부분만 수정한 패치와 함께 회귀 테스트 결과, 배포 및 롤백 인계 자료를 제출하세요.
Codex Cloud에 민감한 정보를 제거한 장애 조사 자료와 정상 작동하는 테스트 환경을 제공하세요. 증거를 조사해 코드 결함을 뒷받침하는 근거가 있으면 결함을 재현하세요. 필요한 부분만 수정한 패치와 함께 회귀 테스트 결과, 배포 및 롤백 인계 자료를 제출하세요.
Related links
Best for
- 장애 상황이 안정된 후 원인을 조사하는 소규모 엔지니어링 팀
- 민감한 정보를 제거한 조사 자료와 안전한 재현용 입력값이 있는 애플리케이션 결함
- 정상 작동하는 격리된 테스트 환경에서 특정 결함의 수정 사항을 검토하는 팀
Starter prompt
미리 준비한 클라우드 환경에서 Codex에 조사 범위가 명확한 인시던트 하나를 맡기세요. 엔지니어가 검토할 수 있도록 근거가 있는 원인 설명, 수정 전에는 실패하는 회귀 테스트, 해당 결함에 집중한 패치, 실제로 실행한 테스트 결과를 확보하는 것이 목표입니다.
이 방식은 긴급 대응으로 서비스를 안정화한 뒤 애플리케이션 결함을 파악하고 수정할 때 적합합니다. 확보한 증거로 원인을 확정할 수 없다면 조사한 내용과 다음 진단 단계를 명확히 정리하는 것만으로도 의미가 있습니다.
환경과 인시던트 자료 준비하기
-
웹 또는 데스크톱 앱에서 작업 위치 > 클라우드 메뉴를 열고 게시된 환경을 선택하세요.
-
새 환경이 필요하면 환경 만들기를 선택한 다음 사용할 GitHub 리포지터리를 선택하고 Codex에 런타임, 종속성, 격리된 테스트 서비스를 준비해 달라고 요청하세요.
-
설정 보고서를 검토하고 검사에서 발견된 문제를 해결한 뒤 게시를 선택하세요. 환경을 게시한 다음 새 작업을 시작하세요.
- 최신 클라우드 환경 가이드를 따르세요.
- 리포지터리 지침은 AGENTS.md에 작성하세요.
종속성이나 서비스 설정이 바뀌면 재사용할 환경을 수정하고 테스트한 뒤 다시 게시를 선택하세요. 새 작업에서 업데이트된 환경을 검증하세요.
리포지터리를 새로 고쳐도 설치 및 시작 명령어는 다시 실행되지 않으며, 기존 작업은 각자의 상태를 유지합니다.
접속해야 하는 네트워크 대상과 테스트용 자격 증명은 별도로 설정하세요. 대상에 대한 네트워크 연결을 허용해도 서비스 접근 권한까지 부여되지는 않습니다.
새 작업에서 문제가 발생한 버전의 코드를 체크아웃해 종속성, 검사, 일회용 테스트 데이터베이스가 정상적으로 작동하는지 확인하세요. 시작 커밋과 수정 전 테스트 결과를 기록하세요.
초기 조사에 필요한 자료를 한곳에 모으세요. 민감 정보를 제거한 자료를 리포지터리에 두거나 작업에서 해당 파일에 접근할 수 있게 하세요. 시작 전에 엔지니어가 다음 자료를 준비하고 확인해야 합니다.
| 입력 자료 | 포함할 내용 |
|---|---|
| 인시던트 요약 | 인시던트 ID, UTC 기준 발생 시간대, 예상 동작과 관찰된 동작, 파악된 영향, 현재 상태. 확인된 건수와 추정치를 구분하세요. |
| 릴리스 식별 정보 | 배포된 커밋 SHA, 배포 시각, 비밀 정보를 제외한 관련 설정값. 확인할 수 있다면 마지막으로 정상 동작한 릴리스도 포함하세요. |
| 서로 연관된 증거 | 타임스탬프, 이벤트 ID, 요청 ID, 트레이스 ID, 파일 참조가 포함된 로그와 트레이스 발췌문. 민감 정보를 제거할 때도 동일한 식별자가 자료 전체에서 일치하도록 하세요. |
| 안전한 재현 방법 | 테스트 요청 본문, 관련 픽스처, 운영 서비스를 호출하지 않고 의심되는 코드 경로를 실행하는 방법. |
| 리포지터리 지침 | 관련 경로, 데이터베이스 엔진, 테스트 명령어, 허용되는 변경 사항, 결과를 검토할 엔지니어. |
자료에 액세스 토큰, 고객 페이로드, 개인 정보를 포함하지 마세요.
모니터링 연동은 선택 사항입니다. 연동한다면 해당 프로젝트와 시간대에 한해 읽기 전용 권한을 부여하세요. 파일 기반 워크플로우에는 연동이 필요하지 않습니다. 리포지터리 지침에는 테스트용 자격 증명을 프로덕션 설정과 분리하도록 명시하세요.
시작 프롬프트 실행하기
이 페이지의 시작 프롬프트에서 자리표시자를 인시던트 ID, 리포지터리, 자료 경로로 바꿔 실행하세요. 첫 결과에는 증거가 어느 코드와 관련되는지, 어떤 동작을 재현할 수 있는지, 어떤 정보가 부족한지가 드러나야 합니다.
증거가 코드 결함을 뒷받침할 때만 Codex에 수정까지 진행해 달라고 요청하세요. 그렇지 않으면 조사 결과를 검토하고 부족한 자료를 제공하세요. 설명이 그럴듯하거나 최근에 코드가 바뀌었거나 다른 릴리스에서 테스트가 성공했다는 사실만으로는 원인을 확정할 수 없습니다.
예시: 웹훅 재시도로 주문이 중복 생성되는 경우
실행 가능한 인시던트 샘플로 조사를 연습하세요. 샘플에는 미리 준비한 인시던트 자료, 결함을 재현할 수 있는 핸들러, 수정된 핸들러, PostgreSQL 테스트가 포함되어 있습니다.
서비스는 order.created 웹훅을 받으면 주문을 저장하고 이벤트 처리 사실을 기록합니다. 인시던트가 발생했을 때 재시도로 두 번째 주문이 생성된 정황이 발견되었습니다. 팀은 이미 서비스를 안정화했고 이제 검토할 패치가 필요합니다.
샘플은 JavaScript, Node.js 테스트 러너, PostgreSQL을 사용합니다. 일회용 픽스처의 event_receipts 테이블에서 식별 열은 null을 허용하지 않으며, (provider, account_id, event_id)에는 제약 조건 검사를 지연할 수 없는 고유 키가 설정되어 있습니다.
이 스키마에서는 주문이 생성되기 전에 수신 기록을 선점할 수 있습니다. 또한 정규화된 페이로드의 지문을 보존하므로 같은 이벤트 키가 서로 다른 내용에 재사용되었는지 감지할 수 있습니다.
애플리케이션에서 선점 시점에 주문 외래 키가 필요하거나 충돌을 판별할 증거가 없다면 작업을 중단하고 스키마나 설계를 별도로 결정하세요.
증거 자료에 정확한 런타임, 종속성 잠금 정보, PostgreSQL 버전, 트랜잭션 격리 수준, 수신 기록 스키마를 기록하세요. 다운로드할 수 있는 픽스처의 버전은 Node.js 24.19.0, PostgreSQL 17.6, pg 8.23.1로 고정되어 있습니다.
제공된 Compose 구성을 사용하려면 Docker 데몬이 작동하는 환경을 준비하고 게시 전에 docker info가 정상적으로 실행되는지 확인하세요. 데이터베이스는 공개된 테스트용 자격 증명만 허용하며 로컬 루프백 인터페이스에 바인딩되어 있습니다. 테스트를 적용할 때는 실제 애플리케이션의 데이터베이스 엔진과 관련 설정을 사용하세요.
이 예시는 PostgreSQL의 READ COMMITTED 격리 수준을 사용하고, 제공자의 이벤트 ID가 재전송 시에도 유지되며, 수신 기록 보존 기간이 제공자의 재시도 및 재전송 가능 기간 전체를 포함한다고 가정합니다.
수정된 핸들러는 수신 기록과 주문을 하나의 데이터베이스 트랜잭션에서 저장합니다. 픽스처는 일회용 데이터베이스와 delivery_holds 테이블을 생성해 기존 큐의 격리 절차를 모사합니다. 이 테이블은 테스트 인프라에만 사용하며 애플리케이션 마이그레이션 대상에 포함하지 않습니다.
이 수정으로 애플리케이션에 큐, 데이터베이스 서비스, 스키마 마이그레이션, 모니터링 연동이 추가되지는 않습니다. 전제 조건이 맞지 않으면 작업을 중단하고 범위를 다시 정하세요.
샘플 실행하기
ZIP 파일의 압축을 풀고 해당 디렉터리로 이동한 뒤 Docker와 Compose로 다음 명령어를 실행하세요.
docker compose -p codex-incident-rehearsal up -d --wait postgres
docker compose -p codex-incident-rehearsal run --rm runner node main.mjs baseline
docker compose -p codex-incident-rehearsal run --rm runner node main.mjs regression
docker compose -p codex-incident-rehearsal run --rm runner
docker compose -p codex-incident-rehearsal down --volumes
수정 전 기준 테스트 명령어는 종료 코드 1을 반환해야 합니다. 재시도 후 주문과 수신 기록이 한 건씩 있어야 한다는 검증에서 주문 두 건과 수신 기록 한 건이 관찰되기 때문입니다. 회귀 테스트 명령어는 수정된 핸들러에서 같은 조건을 검증하며 종료 코드 0을 반환해야 합니다.
마지막 테스트 명령어는 독립적인 연결, 데이터베이스 잠금 경합, 수신 기록이 없는 기존 주문의 격리를 포함한 전체 테스트 조합을 실행합니다.
설치된 명령어가 docker-compose라면 docker compose 대신 해당 표기를 사용하세요. 명령어는 순서대로 실행하세요. 테스트 모음은 테스트용으로 표시한 스키마만 초기화합니다.
트랜잭션 핸들러 살펴보기
아래 코드는 다운로드 파일의 main.mjs에서 발췌했습니다. ZIP 파일의 압축을 푼 뒤 전체 파일을 실행하세요. 테스트와 픽스처 설정도 함께 들어 있습니다.
수신 기록 선점과 주문 저장 트랜잭션
async function fixed(client, input, hook = async (_boundary) => {}) {
await assertNotHeld(client, input);
await hook("beforeClaim");
await client.query("BEGIN ISOLATION LEVEL READ COMMITTED");
let committed = false;
try {
const claimed = await client.query(
`INSERT INTO event_receipts(provider, account_id, event_id, payload_fingerprint)
VALUES ($1,$2,$3,$4)
ON CONFLICT (provider, account_id, event_id) DO NOTHING RETURNING event_id`,
[...key(input), fingerprint(input)]
);
trace("receipt-claim-result", client, input, {
inserted: claimed.rowCount === 1,
});
if (claimed.rowCount === 1) {
await hook("afterClaim");
await writeOrder(client, input);
await hook("afterOrder");
} else {
// A new READ COMMITTED statement sees the winner after its commit.
await checkReceipt(client, input);
}
await client.query("COMMIT");
committed = true;
trace("transaction-committed", client, input);
await hook("afterCommit");
trace("acknowledged", client, input, acknowledgment);
return acknowledgment;
} catch (error) {
if (!committed) {
await client.query("ROLLBACK");
trace("transaction-rolled-back-no-ack", client, input);
} else {
trace("committed-response-lost-no-ack", client, input);
}
throw error;
}
}1. 핵심 조사 자료 제공하기
다음 파일 구성을 참고해 자료를 준비하세요.
incidents/INC-DEMO-014/incident.md
incidents/INC-DEMO-014/logs.ndjson
incidents/INC-DEMO-014/release.json
incidents/INC-DEMO-014/order-created.sanitized.json
이 시나리오에서 release.json에 기록된 예시 릴리스의 SHA는 4a1c9e7d2b6f8a0c3d5e7f9012a4b6c8d0e2f413이고 배포 시각은 2026-09-22T11:58:00Z입니다. 인시던트 발생 시간대는 같은 날 12:03:00Z–12:05:00Z입니다. 자료에는 제공자 이벤트 한 건에 내부 주문 두 건이 연결되어 있다고 기록되어 있습니다. 직접 자료를 준비할 때는 확인된 영향 건수를 포함하고 추정치는 따로 표시하세요.
민감 정보를 제거한 로그에서 서로 연관된 다음 동작을 확인할 수 있습니다.
| UTC 시각 | 연관 정보 | 관찰 내용 |
|---|---|---|
| 12:03:04.110 | 이벤트 evt_demo_042, 계정 acct_demo_01, 요청 req_demo_a, 트레이스 trace_demo_a |
첫 번째 전송이 웹훅 핸들러에 도달합니다. |
| 12:03:04.145 | 같은 이벤트와 첫 번째 트레이스 | 주문 ord_demo_101이 커밋됩니다. |
| 12:03:04.150 | 같은 워커 | 수신 기록 저장에 성공했다는 로그나 응답 기록이 남기 전에 워커가 종료됩니다. 발췌문만으로는 수신 기록이 없다고 단정할 수 없습니다. |
| 12:03:14.203 | 같은 제공자/계정/이벤트, 요청 req_demo_b, 트레이스 trace_demo_b |
재시도 요청이 핸들러에 도달합니다. 핸들러는 일치하는 수신 기록을 찾지 못했다고 로그에 남깁니다. |
| 12:03:14.241 | 같은 이벤트와 두 번째 트레이스 | 주문 ord_demo_102가 커밋되고 수신 기록이 저장됩니다. |
같은 이벤트를 여러 번 전송해도 이벤트 ID는 유지하고, 요청 ID와 트레이스 ID는 전송마다 구분하세요. 요청별 식별자로 이벤트를 식별하면 재시도와 원래 이벤트의 관계를 파악하기 어렵습니다.
2. 증거를 추적하고 가설 검증하기
인시던트 보고서는 재시도 폭주로 주문이 중복되었을 가능성을 제기합니다. Codex는 이를 가설로 다뤄야 합니다. 전송 두 건이 관찰되었다고 해서 폭주가 입증되지는 않습니다. 재시도 자체는 웹훅 핸들러가 정상적으로 처리해야 하는 상황입니다.
샘플에서 문제가 발생한 코드를 살펴보면 수신 기록을 조회하고 주문을 커밋한 뒤 별도 트랜잭션에서 수신 기록을 저장합니다. 두 커밋 사이의 공백이 중복 주문을 더 잘 설명합니다. 이때 워커에 장애가 발생하면 주문만 남고 수신 기록은 없어 재시도 시 주문이 하나 더 생성될 수 있습니다. 동시에 두 요청이 들어올 때도 어느 쪽이든 수신 기록을 저장하기 전에 두 요청 모두 사전 검사를 통과할 수 있습니다.
Codex에 실제 핸들러와 트랜잭션 경계를 추적하고 관련 코드 줄을 인용해 달라고 요청하세요. Codex는 조사 자료의 릴리스와 테스트 대상 코드가 일치하는지 확인하고, 다른 커밋을 테스트했다면 결론에 어떤 한계가 있는지 설명해야 합니다.
3. 핸들러를 변경하기 전에 결함 재현하기
샘플 웹훅과 격리된 PostgreSQL 데이터베이스로 회귀 테스트를 추가하세요. 기존 핸들러에서 주문 저장 직후, 수신 기록을 저장하기 전에 테스트 전용 장애 주입 훅을 넣으세요.
실제 트랜잭션 경계를 확인해 해당 시점에 원래 주문이 이미 커밋되었는지 검증하세요. 같은 제공자/계정/이벤트로 재시도하고 주문 한 건과 그에 일치하는 수신 기록 한 건만 존재하는지 검증하세요. 결함을 심어 둔 구현에서는 주문이 두 건 생성되어 테스트가 실패해야 합니다.
수정 후에는 주문 저장문 실행 직후, 두 쓰기를 묶은 트랜잭션이 커밋되기 전에 대응하는 훅을 실행하세요. 시도가 실패하면 주문과 수신 기록 모두 0건이어야 합니다.
재시도 후 각각 한 건인지 검증하세요. 버전이 바뀌어도 검증하는 비즈니스 조건은 동일하게 유지하고 각 버전에서 훅의 위치를 기록하세요.
새 테스트는 먼저 기존 구현에서 실행하세요. 이 샘플에 심어 둔 결함이 재현되면 주문이 한 건이 아니라 두 건이어서 테스트가 실패합니다. 명령어, 시작 커밋, 종료 코드, 검증 결과 출력을 저장하세요. 예상한 실패가 발생하지 않으면 핸들러를 수정하기 전에 결과가 다른 이유를 조사하세요.
예외 주입으로 확인할 수 있는 것은 애플리케이션의 트랜잭션 동작입니다. 가능한 모든 호스트, 네트워크, 데이터베이스 장애를 재현한 것은 아니라는 점을 기록하세요.
4. 증거가 뒷받침하는 최소한의 수정 적용하기
이 결함을 수정하려면 하나의 데이터베이스 연결에서 하나의 트랜잭션으로 이벤트 수신 기록을 선점하고 주문을 저장하세요. 기존 고유 키와 INSERT ... ON CONFLICT (provider, account_id, event_id) DO NOTHING RETURNING ...를 사용하세요.
수신 기록을 삽입한 요청만 주문을 저장할 수 있습니다. 커밋이 끝난 뒤에만 성공 응답을 보내세요. 트랜잭션이 실패하면 두 쓰기 모두 롤백됩니다. 고유성 제약 위반 오류를 잡은 뒤 이미 중단된 트랜잭션을 계속 사용하지 마세요.
경합하는 요청은 첫 번째 트랜잭션이 끝날 때까지 대기할 수 있습니다. 첫 번째 트랜잭션이 커밋되면 두 번째 선점 시도에서는 아무 행도 삽입되지 않습니다. READ COMMITTED에서 새 SELECT를 실행하고 저장된 이벤트 식별 정보와 페이로드가 정책에 부합하는지 확인한 뒤, 주문을 저장하지 않고 기존 중복 처리 응답을 반환하세요.
첫 번째 트랜잭션이 롤백되면 경합하던 요청이 이벤트를 선점해 처리할 수 있습니다. 삽입을 건너뛴 뒤 수신 기록이 없거나, 식별 정보가 충돌하거나, 일시적인 데이터베이스 오류가 발생하면 성공한 것으로 응답하지 말고 문서화된 재시도 또는 오류 처리 경로를 따르세요.
격리 수준이 더 엄격하면 직렬화 실패 시 리포지터리에 정의된 횟수 제한에 따라 트랜잭션 전체를 재시도해야 합니다. PostgreSQL 문서의 INSERT 충돌 처리 방식과 트랜잭션 격리를 참고하세요.
메모리 내 집합이나 조회와 삽입을 분리한 방식으로는 여러 워커가 동시에 실행되거나 재시작될 때 중복을 막을 수 없습니다. 제공자의 이벤트 식별 규약에 맞춰 키의 범위를 정하세요. 같은 이벤트로 보이는데 식별 정보나 페이로드가 서로 다르면 정상적인 중복으로 처리하지 말고 불일치를 명시하세요.
데모에서 비즈니스 데이터 변경은 모두 같은 데이터베이스 안에서 이루어집니다. 실제 핸들러가 이메일 발송, 카드 결제, 다른 서비스 호출까지 수행한다면 이 트랜잭션만으로는 그런 외부 사이드 이펙트의 멱등성을 보장할 수 없습니다. 수정 범위를 넓히기 전에 트랜잭션의 경계와 시스템에서 이미 사용하는 전송 또는 멱등성 보장 방식을 확인하세요.
5. 변경된 동작 검증하기
수정 전 실패 결과와 수정 후 결과를 모두 요청하세요. 명시한 트랜잭션 경계에 테스트 전용 훅을 두고 별도 연결에서 커밋된 상태를 확인하세요. 샘플은 로컬 PostgreSQL 테스트 11개를 통과합니다. 다운로드 파일의 test-evidence.md에는 그 결과와 클라우드 실행 및 배포를 위해 아직 필요한 검사가 기록되어 있습니다. 게시한 환경에서 테스트 모음을 실행하고 커밋 중에 실제로 연결이 끊기는 경우를 테스트한 뒤에 해당 복구 경로를 사용하세요.
| 검사 항목 / 장애 시점 | 충족해야 할 동작 |
|---|---|
| 최초 전송과 커밋 후 중복 전송 | 최초 전송은 주문과 수신 기록을 한 건씩 생성합니다. 같은 이벤트가 다시 들어오면 약속된 응답을 반환하고 건수는 유지합니다. 정확한 응답 상태와 본문을 기록하세요. |
| 수신 기록 선점 전 | 실패하면 어느 행도 생성되지 않고 성공 응답도 반환되지 않습니다. 재시도하면 각각 한 건이 생성됩니다. |
| 선점 후, 주문 저장 전 | 실패하면 수신 기록이 롤백되고 주문은 존재하지 않습니다. 재시도하면 정상적으로 선점하고 처리할 수 있습니다. |
| 주문 저장 후, 커밋 전 | 실패하면 두 행이 모두 롤백됩니다. 이는 기존 코드에서 주문만 커밋되고 수신 기록은 누락되었던 시점에 대응하는 검사입니다. 수정된 코드에서 재시도하면 각각 한 건만 남습니다. |
| 커밋 후, 응답 전 | 응답 유실을 시뮬레이션하세요. 두 행 모두 커밋된 상태로 남습니다. 재시도하면 주문을 추가하지 않고 중복 처리 응답을 반환합니다. |
| 커밋 결과를 알 수 없는 경우 | 커밋 전후의 연결 끊김을 일반 예외와 별도로 모사하세요. 근거 없이 성공 응답을 반환해서는 안 됩니다. 첫 번째 트랜잭션의 커밋 또는 롤백 여부와 관계없이 같은 이벤트 키로 안전하게 재시도할 수 있어야 합니다. 이 통신 장애를 테스트하지 않았다면 미실행으로 표시하세요. |
| 동시 중복 전송: 첫 번째 트랜잭션 커밋 | 독립된 연결 또는 워커 두 개가 같은 이벤트 키를 두고 경합하게 하세요. 먼저 선점한 워커를 대기시키고 대기를 해제하기 전에 다른 워커를 시작하세요. 실제 경합이 발생했는지 확인한 뒤 먼저 선점한 워커의 대기를 해제하세요. 두 요청 모두 예상한 응답 경로로 완료되고 주문과 수신 기록은 정확히 한 건씩 존재해야 합니다. |
| 동시 중복 전송: 첫 번째 트랜잭션 롤백 | 같은 방식으로 두 요청의 실행을 겹치게 하되 먼저 선점한 쪽을 커밋 전에 실패시키세요. 롤백 후 다른 요청이 선점해 주문과 수신 기록을 한 건씩 생성합니다. 두 응답과 최종 상태를 확인하세요. |
| 이벤트 식별 정보 및 페이로드 충돌 | 서로 다른 이벤트는 독립적으로 처리되어야 하며, 이벤트 ID가 같더라도 계정이나 제공자가 다르면 독립적으로 처리되어야 합니다. 같은 범위에서 ID가 같지만 정규화된 페이로드가 충돌하면 합의된 충돌 처리 경로를 따르세요. 두 번째 주문을 만들거나 서로 다른 작업을 같은 작업으로 간주해 성공 응답을 보내지 마세요. |
| 수신 기록 없는 기존 주문과 대기 중인 재시도 | 수신 기록 없이 이미 커밋된 기존 주문을 테스트 데이터로 넣으세요. 재시도를 재개하기 전에 해당 이벤트 키를 식별해 보류하세요. 보류된 재시도에는 성공 응답을 반환하지 않고 행도 생성하지 않습니다. 신뢰할 수 있는 증거를 대조해 정합성을 복구한 뒤에는 기존 주문 한 건과 수신 기록 한 건을 유지하면서 중복 처리 응답을 반환할 수 있습니다. 증거가 없거나 충돌하면 키를 계속 보류하세요. |
| 서비스 전반 검사 | 관련 통합 테스트, 린트, 타입 검사를 실행하세요. 새로 발생한 실패, 기존 실패, 실행하지 못한 검사를 구분하세요. |
동시 실행을 조율할 때는 배리어나 래치와 함께 타임아웃을 설정하세요. 일정 시간 대기하거나 프로미스 두 개를 실행하는 것만으로는 충분하지 않습니다. 테스트 정리 단계에서 차단된 워커를 해제하고 일회용 픽스처를 사용하세요. 예외 테스트만으로는 프로세스의 갑작스러운 종료, 통신 단절, 프로덕션에서의 동시 실행 동작을 검증할 수 없습니다.
샘플은 버전이 고정된 러너에서 node main.mjs baseline, node main.mjs regression, node main.mjs를 실행합니다. 구조화된 출력에는 트랜잭션 경계, 커밋된 행 수, 응답 상태와 본문, 다른 연결을 실제로 차단한 백엔드의 ID가 기록됩니다.
워크플로우를 적용할 때는 리포지터리에 실제로 정의된 명령어를 사용하고 실행 결과를 그대로 보고하세요. 건너뛴 검사나 사용할 수 없었던 종속성은 결과의 신뢰도에 미치는 영향과 함께 인계 자료에 남기세요.
인계 자료 검토하기
공통 증거 기록
각 인수 기준에 인시던트 워크플로우의 증거 양식을 적용하세요. 아래 항목은 작성용 템플릿이므로 실제 관찰한 출력으로 채워야 합니다. 템플릿 자체가 통과 결과를 나타내지는 않습니다. 기준 버전에 없는 새 테스트는 ‘아직 없음’으로 표시해 기존 검사 실패와 구분하세요.
Criterion / test: [specific behavior and test location]
Environment: [published setup reference; runtime, database and dependency versions]
Before: [revision + test-only diff ID; command; exit code; observed assertion; log path]
After: [final revision or base + diff ID; command; exit code; observed assertion; log path]
Status: [pass / fail / blocked / not run]
Limits / reviewer action: [untested behavior, blocker, or decision required]
실패한 실행과 통과한 실행 각각에 실제 테스트한 코드 상태를 연결하세요. 기존 버전에 테스트만 추가한 변경 사항도 포함하세요. 로그, 응답 픽스처, 최종 diff를 인계 자료와 함께 보관하세요. 최종 버전이 바뀌면 증거도 갱신해야 합니다. 실행하지 않았거나 차단된 검사는 통과로 집계할 수 없습니다.
Codex에 코드 diff와 다음 검토 자료를 리포지터리에 남겨 달라고 요청하세요.
incident-investigation.md: 타임라인, 증거 위치, 검증한 가설, 증거가 뒷받침하는 원인, 아직 불확실한 부분.test-evidence.md: 기준 커밋, 실패한 회귀 테스트 출력, 수정 커밋 또는 diff, 통과한 결함 관련 검사, 서비스 전반 검사, 검증의 한계.pr-draft.md: 결함, 변경된 동작, 테스트 증거, 남은 위험에 대한 간략한 설명.deployment-handoff.md: 검증한 스키마·격리 수준·수신 기록 보존 전제, 롤아웃 담당자 이름, 배포 전 검사, 중복 주문 및 응답·오류 지표, 아래의 롤아웃 및 롤백 조건.
패치를 승인하기 전에 기존 코드에서 새 테스트가 실패하고 수정 후에는 통과하며, diff가 재현된 결함을 해결하는지 확인하세요. 웹훅 예시에서는 이벤트 키의 범위, 트랜잭션 경계, 동시 실행 테스트 범위, 트랜잭션 외부의 사이드 이펙트를 살펴보세요. 기존 중복 주문은 별도로 검토하세요. 새 중복을 방지해도 과거 기록까지 복구되지는 않습니다.
배포와 롤백 계획하기
코드 패치는 수정된 핸들러를 실행하는 워커에서만 효력이 있습니다. 이전 버전의 워커는 여전히 수신 기록을 선점하기 전에 주문을 저장할 수 있으므로 여러 버전이 섞인 카나리 배포 결과만으로 결함이 사라졌다고 판단할 수 없습니다.
배포 담당자는 검증된 혼합 버전 배포 전략을 선택하거나, 수신을 일시 중지하고 이전 워커의 진행 중인 처리가 끝날 때까지 기다린 뒤 모든 컨슈머를 업데이트하고 보관된 전송 건의 처리를 유실 없이 재개하는 기존 런북을 따라야 합니다.
후자를 선택하기 전에 제공자와 큐의 재시도 및 보존 방식을 확인하세요. 일시 중지해도 유실이 없을 것이라고 가정하지 마세요.
보관된 전송 건의 처리를 재개하기 전에 일치하는 수신 기록 없이 커밋된 기존 주문을 식별하세요. 이전 컨슈머를 일시 중지하고 진행 중인 처리를 마치면 이전 방식으로 새 주문이 저장되는 것은 막을 수 있지만 기존 주문까지 복구되지는 않습니다. 기존 큐나 격리 절차를 사용해 영향을 받은 모든 이벤트 키를 보류하세요. 기존 기록에서 완전한 이벤트 식별 정보를 확인할 수 없다면 조사가 끝났다고 판단하지 마세요. 담당자가 검토할 때까지 해당 전송 건이나 영향을 받은 시간대의 전송 건을 계속 보류하세요.
신뢰할 수 있고 변경 불가능한 제공자·계정·이벤트 식별 정보, 정규화된 페이로드 증거, 커밋된 비즈니스 기록을 대조해 이미 처리된 내용을 확인할 수 있을 때만 보류된 키의 정합성을 복구하세요.
승인된 정합성 복구 절차에 따라 비즈니스 데이터를 다시 변경하지 않고 일치하는 수신 기록을 저장한 뒤 해당 키의 보류를 해제하세요.
증거가 없거나 페이로드가 충돌하거나 비즈니스 기록이 여러 건이면 담당자가 검토할 때까지 해당 키를 격리하세요. 원래 이벤트의 지문을 복원할 때 재시도 페이로드로 원래 이벤트에 대한 신뢰할 수 있는 증거를 대신할 수 없습니다.
영향을 받은 키마다 정합성을 복구했거나 모든 컨슈머에서 해당 키의 보류를 계속 적용하는 경우에만 처리를 재개할 수 있습니다.
스테이징에 기존 주문과 대기 중인 재시도 건을 테스트 데이터로 넣으세요. 보류 중에는 추가 주문이 생성되지 않고 성공 응답도 반환되지 않는지 확인하세요. 신뢰할 수 있는 증거로 정합성을 복구한 뒤에는 기존 주문 수가 한 건으로 유지되면서 재시도 요청에 중복 처리 응답이 반환될 수 있습니다. 해결되지 않은 사례도 연습하세요. 근거 없는 수신 기록을 만들지 않고 보류 상태를 유지해야 합니다. 이 재개 조건을 일시 중지 및 진행 중인 처리 완료, 컨슈머 버전, 보존 기간, 롤백에 관한 검사와 함께 배포 인계 자료에 남기세요.
롤아웃 전에 기존 고유성 제약 조건이 존재하고 유효한지, 모든 컨슈머에서 수신 기록 키와 페이로드 정책이 일관되는지, 수신 기록 정리로 재전송된 이벤트가 다시 처리될 여지가 없는지, 외부 사이드 이펙트가 이 패치의 범위 밖에 있는지 확인하세요.
스테이징에서 선택한 배포 순서와 워커 장애 후 재시도를 연습하세요. 담당자가 관찰할 기간과 중복 주문, 웹훅 오류, 응답 지연 시간, 잠금 대기, 재시도 적체에 대한 중단 임계값을 기록하세요. 프로덕션 임계값을 임의로 정하지 마세요.
이전 핸들러로 되돌리면 중복 주문이 다시 발생할 수 있습니다. 롤백 인계 자료에는 피해 확산을 막는 조치(예: 승인된 일시 중지 및 진행 중인 처리 완료 절차), 안전한 버전, 처리 재개 시점을 결정할 담당자를 명시해야 합니다. 수신 기록은 보존하세요. 재시도 차단을 해제하기 위해 삭제하지 마세요. 과거 중복 주문은 별도 검토를 거쳐 복구해야 합니다.
배포 결정과 프로덕션 복구 조치는 엔지니어가 책임집니다. 격리된 테스트의 통과 결과는 검토에 쓸 증거이며 프로덕션이 정상이라는 증명은 아닙니다. 초안을 검토한 뒤 pull request를 만들고 평소의 CI와 리뷰 절차를 거쳐 팀에서 정한 통제 절차에 따라 배포하세요.
같은 작업에서 이어서 진행하기
불확실한 부분을 해소하거나 인계 자료를 보완하려면 범위를 좁혀 후속 요청을 하세요.
두 요청이 동시에 전송되는 경우와 워커 장애 후 재시도하는 경우를 기준으로 패치를 검토하세요. 테스트로 확인한 동작과 아직 테스트하지 않은 동작을 구분해 주세요. 변경 범위는 이 핸들러와 해당 회귀 테스트로 제한하고, 남은 위험은 인계 자료에 반영하세요.
첫 실행에서 인시던트를 재현하지 못했다면 다음과 같이 요청하세요.
구현을 중단하세요. 증거로 배제할 수 있는 가설과 남은 가설 중 가장 유력한 가설을 나열하세요. 그 가설을 다른 가설과 구별하는 데 필요한 최소한의 추가 자료(민감 정보 제거) 또는 최소 규모의 격리 실험을 제시하세요. 근본 원인은 미확정 상태로 남겨 두세요. Related use cases
검증 모듈 마이그레이션
Codex에 마이그레이션 범위를 명시한 문서와 계약 테스트 픽스처, 미리 설정한 클라우드 환경을 제공하세요
Codex가 사용할 CLI 만들기
Codex에 어느 폴더에서나 실행할 수 있고 레포지토리 스크립트와 조합하거나 파일을 다운로드할 때 쓸 수 있는 CLI를 만들어 달라고 하세요
대규모 코드베이스 이해하기
Codex는 낯선 코드베이스의 구조를 정리하고 각 모듈의 역할과 데이터 흐름을 설명합니다. 코드를 수정하기 전에 어떤 파일을 먼저 읽으면 좋을지도 알려 줍니다.