캐시 히트 시 번역이 멈추는 버그

AI 챗 시스템에서 “이미 응답한 적 있는 질문은 캐시에서 바로 꺼내쓰자” 라는 합리적인 최적화를 했는데, 그 캐시 히트 분기에서만 한국어 자동 번역이 영원히 멈추는 버그가 있었어요.

원인을 추적하니 4단계의 인과 사슬이 있어서, 디버깅 기록을 정리해뒀어요.

현상

캐시 히트 시 메인 응답은 정상 종료됐지만, 후속 자동 번역이 “Generating…” 상태로 계속 매달려 있었어요. 30분 idle timeout이 떨어져야 비로소 사라지는 패턴.

4단계 원인 사슬

  1. 번역 Skill이 “Dialog 모드” 목록에 등록되어 있었음 → 사용자와 대화를 주고받는 Skill로 분류돼 있었어요.

  2. 백엔드는 nondialog: true 파라미터로 dialog 모드 우회 메커니즘을 제공 → 자동 chain 호출에서 쓰라고 만들어둔 백도어.

  3. 프론트엔드 자동 번역 호출 body에 nondialog: true가 누락 → 백도어를 호출하지 않고 그냥 정상 모드로 진입.

  4. 결과: 번역 Skill이 dialog 모드로 시작 → “사용자 입력 기다림” 상태로 첫 턴부터 멈춤 → 답이 안 오니 30분 idle timeout까지 hang

계획된 fix

  • 새 브랜치: fix/cache-hit-translation-stuck (master 기반)
  • 파일: 프론트엔드 자동 번역 트리거 함수
  • 변경: body 객체에 nondialog: true 한 줄 추가
  • 옵션: 백엔드도 “자동 chain 호출 마커”를 받으면 dialog 모드를 자동으로 끄도록 방어 추가

인접 fix와 분리

같은 시기에 작업 중이던 다른 브랜치(fix/skill-error-detection)는 Skill abort 패턴 + skip translation을 다루는 별개 fix예요.

이 캐시 히트 버그는 *“abort가 아니라 정상 종료 후 후속 chain이 멈춤”*이라서 다른 fix로는 잡히지 않더라고요. 별도 MR로 분리해서 가는 게 맞다고 판단했어요.

진행 기록

  • 한 차례 “그냥 push해보자”로 빠르게 올렸다가, “로컬 검증 후 push” 원칙 위반을 본인이 지적당함 → remote + 로컬 둘 다 삭제
  • reflog에 commit이 살아있어서 필요시 부활 가능 — git reflog는 정말 마지막 보험이에요

다음 액션

  1. 로컬 환경에서 실제 캐시 히트 시나리오 재현
  2. fix 적용 후 동일 시나리오로 검증 (한 줄 추가로 hang이 해결되는지)
  3. 검증 완료 후 별도 fix 브랜치로 push

배운 점

  • 버그가 한 줄로 끝나는 fix일수록, 인과 사슬은 길다 — 결국 코드 한 줄 추가로 해결됐지만, 그 한 줄을 찾기까지 4단계의 추적이 필요했어요.
  • 백엔드의 nondialog: true 같은 옵션은 기본값을 안전한 쪽으로 두는 게 맞다 — 자동 호출에서 깜빡 빠뜨릴 가능성이 너무 컸어요.
  • 자동 chain 흐름은 단위 테스트보다 통합 테스트가 훨씬 가치 있다 — 각 함수는 멀쩡한데 합쳐지는 순간 hang이 생기는 패턴이라.