StudyDad Loop 제품을 만들며 배운 운영과 설계를 기록합니다.

FamBlend를 중심으로 실제 구현, 운영 메모, GitHub 포트폴리오를 연결해 쌓아가는 StudyDad의 작업 기록입니다.

성장과 기술/개발과 자동화

RCA 초안을 사실, 가설, 액션아이템으로 나누기

박세식 2026. 7. 24. 22:00

AI 장애 분석 포스트모템 시리즈 8/9

AI가 만든 RCA 초안을 그대로 공유하지 않았습니다. AI는 근거를 잘 모으지만 확정된 사실과 그럴듯한 추론을 같은 톤으로 섞을 때가 있었습니다. 편집에서 계속 붙잡은 건 다섯 가지 분리였습니다.

Fact
Assumption
Hypothesis
Ruled out
Action

Fact — 출처가 있는 것만 남겼습니다

이번 사례의 fact:

  • GET /v3/auth traffic은 baseline 대비 6~10배 증가했습니다.
  • major GC count는 0이었습니다.
  • /heartbeat는 ALB target group health check path였습니다.
  • health check timeout은 30초였습니다.
  • target group slow start는 0초였습니다.
  • R2DBC pool에는 acquire timeout이 없었습니다.

각 fact는 아래 출처와 연결됐습니다.

Datadog trace metric
Datadog JVM metric
AWS ELBv2 target group config
AWS CodeDeploy event
source code

출처가 없으면 fact 섹션에 넣지 않았습니다.

Assumption — 명시하지 않으면 위험했습니다

Assumption은 분석을 진행하기 위해 잠시 두는 전제였습니다. 예를 들면 아래와 같습니다:

sampled span/log가 전체 traffic pattern을 대표한다고 가정한다.

샘플링된 데이터로 전체를 추론하기 때문에 assumption이었습니다. 다만 Assumption을 두는 것 자체는 문제가 아니었습니다. 명시하지 않으면 나중에 fact처럼 보였습니다.

Hypothesis — evidence for/against를 같이 뒀습니다

이번 사례의 초기 hypothesis:

  • GC pause
  • MySQL contention
  • ECS health check amplification
  • provider response anomaly
  • reactive pipeline hang

코드 분석 후 최종 hypothesis는 이렇게 바뀌었습니다.

GET /v3/auth surge
-> R2DBC write pool acquire starvation
-> /heartbeat also waits for same pool
-> ALB timeout
-> ECS replacement loop

각 hypothesis 옆에는 반박 근거를 붙였습니다.

Ruled Out — 왜 아니라고 판단했는지 기록했습니다

무엇을 배제했는지가 남아야 다음 조사 때 같은 논쟁을 반복하지 않았습니다.

Candidate Why ruled out
Full GC pause major GC count/time zero
CPU exhaustion CPU low, thread count stable
Redis bottleneck Redis spans healthy
single-client retry storm broad IP spread, no high-frequency single IP
bot traffic mobile WebView/user sessions, normal status/method distribution
fresh API deploy regression task definition had been in production for days
MySQL as sole root cause DB span/lock wait duration much shorter than app hangs

"아닌 것 같다"만 남으면 ruled out이 아니었습니다.

Action — 구체적이지 않으면 재발 시간을 못 줄였습니다

이런 문장은 재발 방지에 도움이 되지 않았습니다.

모니터링 강화
성능 개선
장애 대응 프로세스 개선

이번 사례에서 실제로 남긴 action:

R2DBC read/write pool에 maxAcquireTime 2초를 설정한다.
GET /heartbeat에서 DB ping을 제거하거나 1초 timeout을 둔다.
AuthController.checkLogin에 request-level timeout을 둔다.
CodeDeploy deployment group에 CloudWatch alarm gate를 연결한다.
Target group slow_start를 30~60초로 설정한다.

Action은 유형으로 나눴습니다.

  • Code
  • Config
  • Infra
  • Observability
  • Process

trigger, mechanism, amplifier, symptom을 나눴습니다

단일 root cause 문장 하나로 끝내지 않았습니다. 장애는 대체로 여러 층이 겹쳐 있었습니다.

Trigger:
Organic mobile traffic surge caused GET /v3/auth to increase 6-10x.

Failure mechanism:
Auth/session lookup used the R2DBC write pool.
The pool had no acquire timeout.
Requests waited indefinitely for a connection.
APM showed long controller spans without SQL child spans because SQL did not start before connection acquisition.

Amplifier:
/heartbeat also used the same DB pool.
ALB health check timed out after 30 seconds.
ECS replaced tasks.
Replacement tasks had slow_start=0 and immediately received full traffic.

Symptoms:
Old Gen increased due to retained suspended request state.
MySQL showed pressure but was not the root cause.

이 네 자리를 따로 두면 나중에 어느 층에 대응 조치를 넣었는지 추적하기 쉬웠습니다.

넓은 표현은 좁혔습니다

아래 표현은 피했습니다.

DB가 문제였다.
서버가 죽었다.
메모리 문제가 있었다.
트래픽이 많았다.

너무 넓어서 재발 시 다시 확인이 안 됐습니다. 그래서 대신 이렇게 썼습니다.

GET /v3/auth traffic increased 6-10x.
Auth/session lookup used the write R2DBC pool.
The R2DBC pool had no acquire timeout.
Heartbeat shared the same pool and missed ALB health checks.
ECS replaced tasks repeatedly because replacement tasks immediately received full traffic.

AI 초안을 볼 때 던진 다섯 가지 질문

편집할 때 문장마다 아래를 확인했습니다.

  1. 이 문장은 어떤 데이터로 확인됐는가?
  2. 이 문장은 가설인가 사실인가?
  3. 반대 증거가 있는가?
  4. 같은 데이터를 다른 방식으로 해석할 수 있는가?
  5. 이 문장을 공개해도 내부 정보가 노출되지 않는가?

이번 사례에서 AI의 첫 해석은 코드 분석 후 일부 수정됐습니다. 처음에는 "long span에 child DB span이 없으니 application Mono가 hang"으로 해석했습니다. 코드를 확인한 뒤에는 이렇게 바뀌었습니다.

connection acquire wait는 SQL span이 시작되기 전이라 APM에 child span이 보이지 않는다.

APM만으로는 첫 해석이 최선이었고, 코드 분석이 들어오면서 더 정확해졌습니다.

다음 조사에서 실제로 바꾼 것

RCA 초안을 볼 때 문장을 매끄럽게 다듬기 전에 이 다섯 분리부터 통과시켰습니다. fact, assumption, hypothesis, ruled out, action이 모두 자기 자리에 있으면 그때 문장을 손봤습니다. 다음 편에서는 이 초안을 내부 채널에 실제로 올리는 워크플로우를 정리합니다.

Next Step 이 글은 StudyDad 작업 루프의 한 조각입니다.

글에서 정리한 생각은 GitHub의 코드와 포트폴리오로 이어지고, 일부는 FamBlend 같은 제품 실험으로 확장됩니다.