109 lines
5.8 KiB
Markdown
109 lines
5.8 KiB
Markdown
---
|
|
tags: [infra, robeing, rb8001, skill-email, naverworks, plans]
|
|
---
|
|
|
|
# 260309 9시 네이버 이메일 분석 실패 은닉 해결 계획
|
|
|
|
## 상위 원칙
|
|
- [Infra Project Identity](../../00_Philosophy/00_IDENTITY/Infra_Project_Identity.md)
|
|
- [Core Infrastructure Principles](../../00_Philosophy/01_PRINCIPLES/Core_Infrastructure_Principles.md)
|
|
- [Operational Guardrails](../../00_Philosophy/02_GUARDRAILS/Operational_Guardrails.md)
|
|
- 공통 작성 원칙: [0_VALUE Writing Principles](https://github.com/happybell80/0_VALUE/blob/main/02_Governance/writing-principles.md)
|
|
|
|
## 관련 문서
|
|
- [Infra Journey](../README.md)
|
|
- [9시 네이버 이메일 분석 미전송과 실패 은닉 이슈](../troubleshooting/260309_9시_네이버이메일분석_미전송_실패은닉_이슈.md)
|
|
- [9시 네이버 이메일 분석 미전송 실패 은닉 리서치](../research/260309_9시_네이버이메일분석_미전송_실패은닉_리서치.md)
|
|
- [24서버 실서비스 운영전환 계획](./260309_24서버_실서비스운영전환_계획.md)
|
|
|
|
## 목표
|
|
- 네이버 이메일 분석 브리핑에서 `메일 조회 실패`와 `실제 메일 0건`을 분리한다.
|
|
- `rb8001` 스케줄러가 실패를 성공처럼 기록하지 않도록 한다.
|
|
- 재발 시 `auth refresh 지연`인지 `NAVER WORKS 외부 API 지연`인지 로그만으로 구분 가능하게 만든다.
|
|
- `expires_at` 시간대 정합성 위험을 별도 판단 가능한 상태로 만든다.
|
|
|
|
## 문제 정의
|
|
- 현재 `rb8001/app/services/skills/naverworks_briefing.py`는 `skill-email /messages` 호출 실패를 `return []`로 삼킨다.
|
|
- 그 결과 `process_briefing()`는 실패를 `No emails in the last 24 hours`로 오인하고 종료한다.
|
|
- 이 구조는 상위 SSOT의 `Truth First`, `근본 원인 직접 수정 우선`, `광범위 예외 폴백 금지` 원칙과 충돌한다.
|
|
|
|
## 범위 고정
|
|
|
|
### 1차 수정 대상
|
|
- `rb8001/app/services/skills/naverworks_briefing.py`
|
|
- `skill-email/main.py`
|
|
- `skill-email/services/naverworks_provider.py`
|
|
|
|
### 1차 검증 대상
|
|
- `rb8001` 09:00 스케줄 실행 경로
|
|
- `skill-email /messages?provider=naverworks`
|
|
- `auth-server /auth/naverworks/passport/refresh`
|
|
|
|
### 2차 판단 대상
|
|
- `main_db.naverworks_token.expires_at` 컬럼 타입 변경 필요 여부
|
|
- 컨테이너 시간대 통일 여부 또는 만료 판단 로직의 시간대 명시 처리
|
|
|
|
## 실행 원칙
|
|
- 실패를 `메일 0건`으로 대체하지 않는다.
|
|
- `except Exception`으로 원인성 실패를 일괄 래핑하지 않는다.
|
|
- `timeout`, `비200 응답`, `응답 구조 이상`, `토큰 갱신 실패`를 구분해 기록한다.
|
|
- 수정 후에는 로그, 헬스체크, 재현 테스트로 실제 실패 표현이 교정됐는지 확인한다.
|
|
|
|
## 단계별 계획
|
|
|
|
### 1. `rb8001`에서 실패 은닉을 제거한다
|
|
- `_fetch_recent_emails()`가 `[]` 대신 원인성 예외를 올리도록 바꾼다.
|
|
- 최소 분기 대상:
|
|
- `httpx.ReadTimeout`
|
|
- `httpx.ConnectError`
|
|
- `비200 응답`
|
|
- 응답 JSON 구조 이상
|
|
- `process_briefing()`는 `실제 0건`일 때만 `No emails`로 처리하고, 조회 실패는 스케줄러 실패로 남기게 바꾼다.
|
|
|
|
### 2. `skill-email` 단계별 원인 로그를 보강한다
|
|
- `/messages` 경로에서 `provider=naverworks` 처리 시 다음 단계 로그를 남긴다.
|
|
- DB 토큰 조회 시작/종료
|
|
- 만료 판단 결과
|
|
- refresh 호출 여부와 응답 코드
|
|
- NAVER WORKS 외부 API 호출 시작/종료와 소요시간
|
|
- 로그는 성공/실패 모두 같은 요청 흐름에서 이어서 대조 가능해야 한다.
|
|
|
|
### 3. `expires_at` 정합성 위험을 코드와 데이터 기준으로 판단한다
|
|
- 현재 `naverworks_token.expires_at`가 `timestamp without time zone`인 상태를 전제로, 실제 만료 판단이 어떤 시간대 기준으로 이뤄지는지 명시한다.
|
|
- `skill-email`의 만료 비교 시각과 DB 기록 시각이 같은 시간대 기준으로 비교되도록 정리한다.
|
|
- 선택지는 둘 중 하나로 고정한다.
|
|
- 컬럼 타입을 `timestamptz`로 승격
|
|
- 비교 전후를 모두 명시 timezone 기준으로 변환
|
|
- 이 단계는 폴백 추가가 아니라, 판단 기준을 하나로 고정하는 작업이어야 한다.
|
|
|
|
### 4. 재현 검증으로 닫는다
|
|
- 정상 케이스:
|
|
- 현재와 같은 `/messages` 요청이 정상 응답하고 브리핑이 실제 전송되는지 확인한다.
|
|
- 실패 케이스:
|
|
- 내부적으로 timeout 또는 비정상 응답을 재현했을 때 스케줄러가 실패로 남는지 확인한다.
|
|
- 토큰 만료 케이스:
|
|
- 만료 상태에서 refresh가 정상 수행되면 성공으로 이어지고, refresh 실패 시 원인성 실패로 남는지 확인한다.
|
|
|
|
## 검증 계획
|
|
|
|
### 1. 코드 검증
|
|
- `rb8001`에 `return []` 기반 실패 은닉 경로가 남아 있지 않은지 확인한다.
|
|
- `skill-email` 로그가 단계별로 남는지 확인한다.
|
|
|
|
### 2. 로그 검증
|
|
- 성공 케이스에서는 `메일 0건`과 `조회 성공 0건`이 구분된다.
|
|
- 실패 케이스에서는 `No emails` 대신 `timeout`, `refresh failed`, `works api failed` 같은 원인 로그가 남는다.
|
|
- 스케줄러 로그에는 실패 시 `completed successfully`가 남지 않는다.
|
|
|
|
### 3. 실행 검증
|
|
- `rb8001`에서 수동 브리핑 실행 시 정상 메일 조회와 Slack 전송이 통과한다.
|
|
- `/messages` 단건 호출로 성공/실패 응답 시간이 확인된다.
|
|
- `auth-server /refresh`와 `skill-email /messages`를 같은 시각대에 대조해 병목 위치를 구분할 수 있다.
|
|
|
|
## 완료 조건
|
|
- 네이버 이메일 조회 실패가 더 이상 `No emails`로 표시되지 않는다.
|
|
- 9시 브리핑 실패 시 스케줄러와 로그가 모두 실패로 기록된다.
|
|
- `skill-email` 로그만으로도 `DB/refresh/외부 API` 중 어느 단계에서 지연됐는지 구분 가능하다.
|
|
- `expires_at` 만료 판단 기준이 시간대 혼선 없이 하나로 설명 가능하다.
|
|
|