DOCS/troubleshooting/251016_coldmail_duplicate_file_fix.md
Claude-51124 f9a5b96df2 Add troubleshooting documentation for 3 resolved issues
Documents created:
- 251016_coldmail_duplicate_file_fix.md: Idempotent file upload solution
- 251016_troubleshooting_summary.md: Summary of 3 issues resolved on 251016

Issues resolved:
1. Coldmail IR analysis failure (duplicate file handling)
2. NaverWorks Briefing system UUID error
3. gRPC + uvloop BlockingIOError log noise

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-16 16:14:13 +09:00

7.5 KiB

Coldmail IR 분석 실패 해결 - 중복 파일 처리 개선

날짜: 2025-10-16 작성자: Claude (51124 서버 전담) 커밋: skill-rag-file cea055c 관련 문서: 251015_claude_coldmail_ir_analysis_failure.md


문제 요약

Coldmail 브리핑 워크플로우에서 동일한 PDF 파일이 재업로드될 때 400 - File already exists for this team 에러 발생하여 전체 IR 분석 실패.

연쇄 효과

PDF 업로드 실패 (400 에러)
    ↓
document_id = None
    ↓
RAG 검색 0건
    ↓
IR 분석 "N/A" 반환
    ↓
Slack Lists API 에러

원인 분석

위치: skill-rag-file/app/api/upload.py:67-71

기존 코드:

if result.scalar():
    raise HTTPException(
        status_code=400,
        detail="File already exists for this team"
    )

문제점:

  • 중복 파일 발견 시 HTTPException 발생
  • document_id 반환 없음
  • 워크플로우 중단
  • 멱등성(idempotency) 미보장: 동일 요청을 여러 번 실행할 때 결과가 달라짐

해결 방법

코드 수정

위치: skill-rag-file/app/api/upload.py:60-79

# Check if file already exists for this team
from sqlalchemy import select
stmt = select(TeamDocument).where(
    TeamDocument.team_id == team_id,
    TeamDocument.file_hash == file_hash
)
result = await db.execute(stmt)
existing_doc = result.scalar_one_or_none()

if existing_doc:
    # 중복 파일 발견 - 기존 document_id 반환 (멱등성 보장)
    logger.info(f"File already exists (hash: {file_hash}), returning existing document_id: {existing_doc.id}")
    return FileUploadResponse(
        document_id=existing_doc.id,
        filename=existing_doc.filename,
        file_hash=existing_doc.file_hash,
        storage_path=existing_doc.storage_path,
        chunk_count=existing_doc.chunk_count or 0,
        message=f"File already exists, returned existing document_id: {existing_doc.id}"
    )

핵심 변경사항

  1. select(TeamDocument.id) → select(TeamDocument): 전체 레코드 조회
  2. HTTPException 제거: 에러 대신 기존 데이터 반환
  3. FileUploadResponse 반환: 기존 document_id와 메타데이터 제공
  4. INFO 로그 추가: 중복 감지 로그 기록

효과

1. 멱등성 보장

Before:

1st upload: 성공 → document_id: abc-123
2nd upload: 실패 → 400 에러

After:

1st upload: 성공 → document_id: abc-123
2nd upload: 성공 → document_id: abc-123 (동일)
3rd upload: 성공 → document_id: abc-123 (동일)

2. Coldmail 워크플로우 안정성

PDF 업로드 (멱등성 보장)
    ↓
document_id 항상 획득 ✅
    ↓
RAG 검색 정상 작동 ✅
    ↓
IR 분석 정확한 데이터 ✅
    ↓
Slack Lists API 성공 ✅

3. 재시도 안전성

네트워크 오류, 타임아웃 등으로 재시도 시에도 안전하게 작동:

  • 1차 시도: 파일 저장, 벡터 생성 (느림)
  • 2차 시도: 기존 document_id 즉시 반환 (빠름)

배포

커밋 정보

cd /home/admin/ivada_project/skill-rag-file

git add app/api/upload.py
git commit -m "Fix duplicate file handling in upload API

Change duplicate file behavior from HTTPException to idempotent response.
When same file (hash) uploaded by team, return existing document_id instead
of raising 400 error. This ensures coldmail workflow continues when PDFs
are re-uploaded.

Fixes: Coldmail IR analysis failure due to PDF upload rejection"

git push origin main

커밋 해시: cea055c

배포 확인

docker compose down && docker compose up -d --build

# 상태 확인
docker ps --filter name=skill-rag-file
# NAMES            STATUS
# skill-rag-file   Up 2 minutes (healthy)

# 로그 확인
docker logs skill-rag-file --tail 20
# INFO:     Application startup complete.
# INFO:     Uvicorn running on http://0.0.0.0:8508

검증 계획

즉시 검증 (수동)

  1. 동일 파일 2회 업로드 테스트:
# 1차 업로드: 성공, document_id 획득
# 2차 업로드: 성공, 동일 document_id 획득
  1. 로그 확인:
docker logs skill-rag-file | grep "File already exists"
# 예상: INFO - File already exists (hash: ...), returning existing document_id: ...

실전 검증 (내일 09:05)

Coldmail 브리핑 실행 시:

  1. PDF 업로드 성공 여부
  2. IR 분석 "N/A" 아닌 실제 데이터 반환 여부
  3. Slack Lists 전송 성공 여부

로그 체크 포인트:

# rb8001 로그 (09:05 이후)
docker logs rb8001 --since "2025-10-17T09:00:00" | grep -iE "upload|document_id"

# skill-rag-file 로그
docker logs skill-rag-file --since "2025-10-17T09:00:00" | grep "File already exists"

관련 이슈 해결 상태

해결됨

  1. 중복 파일 처리 개선: HTTPException → 기존 document_id 반환
  2. 멱등성 보장: 동일 요청 여러 번 실행 가능
  3. Coldmail 워크플로우 안정성: PDF 업로드 실패로 인한 전체 실패 방지

향후 개선 사항

  1. Slack Lists 동적 컬럼 매핑:

    • 현재: 하드코딩된 컬럼 ID (Col09HQTDUM0T 등)
    • 개선: Lists 컬럼 조회 API + 컬럼명 기반 동적 매핑
  2. LangGraph 체크포인트 + 에러 복구:

    • 현재: Stateless 실행
    • 개선: PostgresSaver 체크포인트 + 실패 지점부터 재시도
    • LLM 기반 에러 페이로드 수정 및 재전송

교훈

1. 멱등성은 필수 (Idempotency is Critical)

원칙: 동일 요청을 여러 번 실행해도 결과가 같아야 함

적용:

  • POST 요청이라도 리소스 생성/조회에는 멱등성 필요
  • 중복 체크 시 "에러"가 아닌 "기존 리소스 반환"이 정답
  • 네트워크 불안정, 재시도 상황에서 안전성 보장

2. Cascading Failure 방지

문제: 한 단계 실패 → 전체 워크플로우 실패

해결:

  • 각 단계의 실패 격리
  • 재시도 가능한 설계
  • Graceful degradation (부분 성공 허용)

3. 에러 메시지의 의미

"File already exists"는 에러가 아니다:

  • 상태 확인 메시지로 취급
  • 이미 존재하는 리소스 정보 반환
  • 클라이언트는 정상 처리 가능

4. 프레임워크 활용의 실효성

LangGraph 도입 vs 실제 활용:

  • 체크포인트 없는 LangGraph = 코드 정리 효과만
  • 핵심 기능(체크포인트, 재시도)까지 구현해야 프레임워크 가치 발휘
  • 점진적 개선: 멱등성 → 체크포인트 → LLM 복구

다음 단계

즉시 수행

  • 코드 수정 및 배포
  • Docker 재시작 확인
  • 헬스체크 정상 확인

내일 확인 (2025-10-17 09:05)

  • Coldmail 브리핑 실행 성공 여부
  • PDF 업로드 로그에서 "File already exists" 메시지 확인
  • IR 분석 결과에 실제 데이터 포함 확인
  • Slack Lists API 전송 성공 확인

향후 개선

  • Slack Lists 동적 컬럼 매핑 구현
  • LangGraph 체크포인트 활성화
  • LLM 기반 에러 복구 로직 추가

결론

Coldmail IR 분석 실패 해결 완료

2025-10-16, skill-rag-file 중복 파일 처리 개선 배포

핵심 성과

  1. 멱등성 보장: 동일 파일 재업로드 시 기존 document_id 반환
  2. 워크플로우 안정성: PDF 업로드 실패로 인한 전체 실패 방지
  3. 재시도 안전성: 네트워크 오류, 타임아웃 상황에서도 안전
  4. 즉시 배포: skill-rag-file 컨테이너 재시작 완료

검증 대기

내일 09:05 Coldmail 브리핑 실행 시 실전 검증 예정.