3.4 KiB
3.4 KiB
9시 네이버 이메일 분석 미전송 실패 은닉 리서치
tags: [naverworks, email, briefing, timeout, failure-observability]
날짜: 2026-03-09
작성자: Codex
상위 원칙: 문서 작성 원칙, Backend Coding Principles
관련 문서
조사 대상
- 왜
2026-03-09 09:00네이버 이메일 분석 브리핑이 전송되지 않았는가 - 왜 실패가 운영 로그에서는 성공처럼 보였는가
- 이번 문제를 우회 없이 닫으려면 무엇을 직접 수정해야 하는가
확인된 사실
rb8001스케줄 로그 기준2026-03-09 09:00:00KST에naverworks_daily작업은 실행됐다.- 같은 요청 흐름에서
2026-03-09 09:00:30KST에httpx.ReadTimeout이 발생했다. rb8001/app/services/skills/naverworks_briefing.py는 메일 조회 실패를 빈 목록처럼 다뤄No emails로 끝낼 수 있는 구조였다.rb8001/app/scheduler/jobs/naverworks_briefing.py는 작업 예외를 다시 올리지 않아 APScheduler 성공 로그와 실제 실패가 어긋날 수 있는 구조였다.skill-email/services/naverworks_provider.py는 당시 단계별 elapsed 로그가 부족해,30초 블로킹의 최종 지점이auth-server refresh인지 NAVER WORKS 외부 API인지 즉시 분리하기 어려웠다.skill-email,auth-server컨테이너는 UTC 기준으로 동작하고,rb8001은 KST 기준 스케줄을 사용한다.main_db.naverworks_token.expires_at는timestamp without time zone이라 비교 기준이 코드에서 명시되지 않으면 해석 흔들림이 생긴다.
해석
- 직접 원인은
rb8001 -> skill-email /messages경로의 read timeout이다. - 구조 원인은 "조회 실패"와 "조회 결과 0건"을 같은 값으로 취급한 설계다.
- 관측 실패 원인은 스케줄러 래퍼가 예외를 다시 올리지 않은 점이다.
- 시간대 혼재와 naive timestamp는 재발 위험을 높이는 구조 요인이지만, 이번 09:00 미전송의 직접 원인 자체를 대체하지는 않는다.
이번 문제를 닫는 수정 기준
- 메일 조회 실패는 빈 목록이 아니라 명시적 실패 타입으로 분리한다.
- 스케줄러는 실패를 다시 올려 운영 로그와 실제 결과를 일치시킨다.
skill-email에는 단계별 추적 로그를 추가해 다음 장애에서 block point를 즉시 좁힐 수 있게 한다.- 토큰 만료 판단은 UTC 기준을 코드에서 명시해 DB 값과 비교 축을 고정한다.
미확정
2026-03-09 09:00의 실제 30초 block point가auth-server refresh였는지, 외부 NAVER WORKS API였는지는 당시 세부 로그 부재로 100% 확정하지 못했다.- 이 항목은 이번 수정 후 재발 시 새 추적 로그로 닫는다.
결론
- 이 문제의 핵심은 "9시 작업이 안 돌았다"가 아니라 "9시 작업 실패가 성공처럼 은닉됐다"는 점이다.
- 따라서 우회 없는 해법은 재시도 강화가 아니라 실패 타입 분리, 예외 재전파, 단계별 추적 로그 추가다.