Skip to content

ADR-12: Phase 2 행동 캐시

🇺🇸 English Version

날짜작성자리포지토리
2026-01-24@KubrickCodeworker, infra

Context

Phase 2 비용 문제

AI 기반 스펙 문서 생성 파이프라인(ADR-14)의 2단계 구조:

Phase모델비용목적
Phase 1gemini-2.5-flash$0.30/1M도메인별 테스트 분류
Phase 2gemini-2.5-flash-lite$0.10/1M테스트명 → 행동 설명 변환

Phase 1 결과는 content_hash 기반 문서 수준 캐싱 적용. Phase 2는 테스트별 AI 호출로 다음 상황에서 비용 증가:

  • 동일 테스트 파일의 다중 커밋 분석
  • 다른 리포지토리의 유사 테스트명
  • 파서 버전 업데이트 또는 사용자 요청에 의한 재분석

캐싱 기회

테스트 행동 설명의 높은 캐시 재사용성:

시나리오예시
동일 테스트, 다른 커밋커밋 A와 B의 test_user_login은 동일 행동 생성
리포지토리 간 유사성testAuthentication 행동은 언어/프레임워크 무관
재분석파서 업그레이드가 행동 의미론에 영향 없음

캐시 키 설계 과제

문제: 테스트 행동의 고유 식별자 결정

접근법장점단점
테스트명만최대 재사용컨텍스트 무시 (동명이테스트 구분 불가)
테스트명 + 파일 경로컨텍스트 인식경로 변경 시 캐시 무효화
테스트 콘텐츠 해시정확한 매칭유사 테스트 간 재사용 불가
의미론적 핑거프린트의도 포착복잡, 추가 AI 호출 필요

Decision

복합 키 (test_name_hash, language, model_id) 및 TTL 기반 만료를 사용하는 PostgreSQL 기반 행동 캐시 구현.

테이블 스키마

sql
CREATE TABLE behavior_caches (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    test_name_hash TEXT NOT NULL,         -- 정규화된 테스트명의 SHA-256
    language VARCHAR(10) NOT NULL,        -- en, ko 등
    model_id VARCHAR(100) NOT NULL,       -- gemini-2.5-flash-lite
    behavior_description TEXT NOT NULL,   -- 캐시된 AI 출력
    confidence DECIMAL(3,2) NOT NULL,     -- 0.00-1.00
    created_at TIMESTAMPTZ DEFAULT NOW(),
    expires_at TIMESTAMPTZ NOT NULL,      -- TTL 만료
    hit_count INTEGER DEFAULT 0,          -- 사용 추적

    CONSTRAINT behavior_caches_unique
        UNIQUE (test_name_hash, language, model_id)
);

-- 조회용 인덱스
CREATE INDEX idx_behavior_caches_lookup
    ON behavior_caches (test_name_hash, language, model_id)
    WHERE expires_at > NOW();

-- 정리용 인덱스
CREATE INDEX idx_behavior_caches_expiry
    ON behavior_caches (expires_at);

캐시 키 전략

test_name_hash = SHA256(normalize(test_name))

normalize(test_name):
  1. 소문자 변환
  2. 공통 접두사 제거 (test_, it_, describe_, should_)
  3. 특수문자 및 숫자 제거
  4. 공백 정리

예시:

원본 테스트명정규화해시 (축약)
test_user_can_loginuser can logina3f2...
TestUserCanLoginuser can logina3f2...
it('should allow user to login')allow user loginb7c1...
describe('User Login')user loginc9e4...

캐시 조회 흐름

┌─────────────────────────────────────────────────────────────────┐
│                    Phase 2 처리                                  │
├─────────────────────────────────────────────────────────────────┤
│  Feature 내 각 테스트에 대해:                                    │
│                                                                  │
│  1. test_name_hash 계산                                          │
│                                                                  │
│  2. 캐시 조회:                                                   │
│     SELECT behavior_description, confidence                      │
│     FROM behavior_caches                                         │
│     WHERE test_name_hash = ? AND language = ? AND model_id = ?   │
│       AND expires_at > NOW()                                     │
│                                                                  │
│  3. HIT 시:                                                      │
│     - hit_count 증가                                             │
│     - 캐시된 행동 반환 (AI 호출 생략)                            │
│     - 쿼터 미소비                                                │
│                                                                  │
│  4. MISS 시:                                                     │
│     - Gemini API 호출                                            │
│     - 결과 TTL과 함께 저장                                       │
│     - 쿼터 소비                                                  │
└─────────────────────────────────────────────────────────────────┘

TTL 설정

티어TTL근거
Free7일제한된 저장 공간, 높은 이탈
Pro30일표준 비즈니스 보존
Pro Plus90일확장 캐싱 가치
Enterprise180일최대 비용 최적화

쿼터 시스템 연동

ADR-13에 따라 캐시 히트는 쿼터 미소비:

go
type Phase2Result struct {
    Behavior   string
    Confidence float64
    FromCache  bool  // true 시 쿼터 미소비
}

// 사용량 추적
if !result.FromCache {
    quotaService.RecordUsage(ctx, userID, QuotaTypeSpecView)
}

Options Considered

Option A: PostgreSQL 테이블 (선택됨)

TTL 및 히트 추적 기능의 데이터베이스 기반 캐시.

장점:

  • 기존 PostgreSQL 인프라와 통합
  • 분석용 쿼리 가능 (히트율, 인기 테스트)
  • 스케줄 작업을 통한 자동 정리
  • 스펙 문서 쓰기와 트랜잭션 일관성

단점:

  • 고볼륨 조회 시 데이터베이스 부하
  • 대용량 캐시의 저장 비용

Option B: Redis 캐시

자동 만료 기능의 인메모리 캐시.

장점:

  • 밀리초 미만 조회
  • 네이티브 TTL 지원
  • 데이터베이스 부하 감소

단점:

  • 추가 인프라 (현재 스택에 없음)
  • Redis 재시작 시 캐시 손실
  • 캐시 크기에 따른 메모리 비용 증가

Option C: 문서 수준 캐싱만

기존 spec_documents.content_hash 캐싱에 의존.

장점:

  • 새 인프라 불필요
  • 이미 구현됨

단점:

  • 리포지토리 간 유사 테스트 재사용 불가
  • 테스트 변경 시 전체 Phase 2 재실행
  • 문서 간 최적화 기회 상실

Option D: 추가 캐싱 없음

Phase 2 비용을 운영 비용으로 수용.

장점:

  • 가장 단순한 구현
  • 캐시 무효화 복잡성 없음

단점:

  • 스케일 시 높은 API 비용
  • 반복 테스트의 느린 응답 시간
  • 고볼륨 사용자의 비용 효율 저하

Consequences

Positive

영역효과
비용 절감Phase 2 API 비용 40-60% 절감 (추정)
응답 시간캐시 히트로 테스트당 1-2초 AI 지연 회피
쿼터 공정성캐시 히트는 사용자 쿼터 미소비
분석히트율 메트릭으로 캐시 튜닝 가능
리포지토리 간유사 테스트의 캐시된 행동 공유

Negative

영역트레이드오프
저장 공간고유 테스트 다양성에 따른 캐시 테이블 증가
오래된 데이터캐시된 행동이 모델 업데이트 미반영 가능
정규화 오류과도한 정규화로 잘못된 매칭 가능
정리 오버헤드TTL 적용을 위한 스케줄 작업 필요

기술 참고

  • 캐시 워밍: 미구현; 캐시는 자연적으로 축적
  • 무효화: 모델 버전 변경은 키의 model_id를 통해 무효화
  • 충돌 해결: 첫 번째 쓰기 우선; 동시 쓰기는 드묾
  • 모니터링: 분석당 캐시 히트/미스 비율 로깅

Configuration

BEHAVIOR_CACHE_ENABLED=true
BEHAVIOR_CACHE_DEFAULT_TTL=30d
BEHAVIOR_CACHE_CLEANUP_SCHEDULE=0 4 * * *  # UTC 04:00 매일
BEHAVIOR_CACHE_CLEANUP_BATCH_SIZE=5000

References

Open-source test coverage insights