Skip to content

v1.11.10

Choose a tag to compare

@github-actions github-actions released this 02 Sep 19:50
· 48 commits to main since this release

Relio v1.11.10 — 분석이 많이 쌓인 계정에서 Signal·Risk·Insight·Recommendation 단건 조회가 "not found" 로 끝나던 문제

Signal, Risk, Insight, Recommendation은 목록에는 보이는데 항목을 열면 "not found"가 나고, 무시·수용·기각도 같은 이유로 실패할 수 있었습니다. 분석 결과가 가장 많이 쌓이는 바쁜 계정에서만 나타나던 문제입니다.

1. 원인

단건 조회 네 곳이 목록 질의를 대신 부르고 그 결과를 Go에서 훑었습니다.

items, err := s.ListSignals(ctx, p, SignalFilter{Status: "ALL", Limit: 200})
for _, item := range items {
    if item.ID == id {
        return item, nil
    }
}
return Signal{}, errors.New("signal not found")

목록은 최대 200건이고, 정렬 기준은 심각도(Signal, Risk)와 점수·우선순위(Insight, Recommendation)입니다. 즉 200건을 넘는 계정에서 그 뒤에 밀린 항목은 이 코드가 볼 수 없는 곳에 있었고, 존재하지 않는 것과 똑같이 취급되었습니다.

읽기만 막힌 것이 아닙니다. IgnoreSignal, AcceptRisk, AcceptRecommendation, DismissRecommendation은 모두 쓰기 전에 대상 레코드를 한 번 읽습니다. 그래서 조언과 위험을 처리하는 동작 전체가 같은 경계에서 함께 실패했습니다.

상황 수정 전 수정 후
정렬 상위 200건 안의 항목 조회 정상 정상
그 뒤에 밀린 항목 조회 not found 정상
그 뒤에 밀린 Signal 무시 / Risk 수용 not found 정상
그 뒤에 밀린 Recommendation 수용 / 기각 not found 정상

영향을 받던 경로는 GET·POST /api/v1/signals/{id}, /api/v1/risks/{id}, /api/v1/insights/{id}, /api/v1/recommendations/{id} 계열이며, 같은 Service를 쓰는 MCP Tool도 동일했습니다.

2. 수정

  • 네 Filter(SignalFilter, RiskFilter, InsightFilter, RecommendationFilter)에 ID 필드를 두고, 각 질의에 기존 필터와 같은 형태의 선택 조건 ($n='' OR x.id::text=$n)을 추가했습니다
  • 단건 조회는 이제 ID를 채우고 Limit: 1로 같은 질의를 부릅니다. 목록과 단건이 한 문장을 공유하므로, 가시성을 결정하는 것은 여전히 Data Scope 조인 하나뿐입니다. 권한이 넓어지지도 좁아지지도 않습니다
  • ID가 비어 있거나 공백뿐이면 조건은 걸리지 않습니다. 목록 동작은 이전과 완전히 동일합니다

3. 재발 방지

각 목록 질의를 (sql, args)를 돌려주는 순수 빌더로 분리해, DB 없이 조건과 인자를 검사할 수 있게 했습니다.

검증

  • 단위 Test 3개 추가: 단건 질의 네 개가 실제로 id Placeholder를 걸고 인자와 대응하는지, 빈·공백 id는 목록을 필터링하지 않는지, 모든 $n이 인자와 1:1로 맞는지 확인하는 회귀 가드
  • id 조건을 지우면 새 Test가 실제로 실패하는지 확인
  • Go 전체 Test/Vet (go test -race ./..., go vet ./...), gofmt
  • TypeScript Typecheck와 React Production Build
  • 환경변수 계약(check-env-contract.sh)과 외부 정적 Asset 차단(check-static-assets.sh)

업그레이드

DB Migration은 없습니다. API 계약과 화면도 그대로이며, 저장된 데이터에는 영향이 없습니다.

지금까지 목록 뒤쪽에 밀려 열리지 않던 항목이 열리고, 무시·수용·기각도 정상 처리됩니다. 이 문제로 처리하지 못하고 남겨 둔 조언과 위험이 있다면 이번 Release 이후 다시 처리할 수 있습니다.

오프라인 이미지

항목
Asset relio-v1.11.10.tar.gz
Docker Image relio:v1.11.10
SHA-256 a76c1bb6760fbbdd5fa4f9c974609531686f3d893c50684a1ac19e57c2e6bc5f
압축 크기 36M
Architecture linux/amd64

이 Release가 직접 제공하는 Asset은 위 Docker Image tar.gz 하나뿐입니다.

오프라인 서버에서 로드

gunzip -c relio-v1.11.10.tar.gz | docker load
docker image inspect relio:v1.11.10

실행

# 최초 1회만 생성하고 비밀번호 관리 도구에 보관하세요.
ENCRYPTION_KEY="$(openssl rand -hex 32)"

docker run -d \
  --name relio \
  -p 8080:8080 \
  -e POSTGRES_DSN="postgres://relio:password@postgres:5432/relio" \
  -e BOOTSTRAP_ADMIN="admin" \
  -e BOOTSTRAP_ADMIN_PASSWORD="ChangeMe-Immediately" \
  -e ENCRYPTION_KEY="$ENCRYPTION_KEY" \
  -v relio-data:/var/lib/relio \
  relio:v1.11.10

Relio Application이 받는 환경변수는 필수 3개(POSTGRES_DSN, BOOTSTRAP_ADMIN, BOOTSTRAP_ADMIN_PASSWORD)와 선택 1개(ENCRYPTION_KEY)뿐입니다.

ENCRYPTION_KEY를 설정하면 Personal Key와 SSO Client Secret이 재기동, 이미지 교체, relio-data Volume 재생성 후에도 유지됩니다. 설정하지 않으면 자격증명을 여는 Key가 Volume 안에만 존재하므로 /var/lib/relio를 PostgreSQL과 같은 복구 시점으로 반드시 보존해야 합니다.

검증

Release 전에 Source Test, TypeScript Typecheck, Frontend Build, Go Vet, 환경변수 계약, 외부 정적 Asset 차단, 비 Root 실행, 완전 격리 Network 신규 설치 Smoke Test, 이전 Release 업그레이드 Test, Docker Save/Load 재검증을 통과합니다.