Skip to content

feat: 리소스 추세 그래프 — 시계열 롤업 + 자동 해상도 (3개월 확대/축소) - #2

Open
xgeekover wants to merge 1 commit into
developfrom
feat/tm-trend-rollup
Open

feat: 리소스 추세 그래프 — 시계열 롤업 + 자동 해상도 (3개월 확대/축소)#2
xgeekover wants to merge 1 commit into
developfrom
feat/tm-trend-rollup

Conversation

@xgeekover

Copy link
Copy Markdown
Owner

배경 (Track B/C)

리소스 위젯 더블클릭 추세 그래프가 브라우저 메모리(historyRef)만 그려서 새로고침마다 초기화되고 최대 120점만 보였습니다. 백엔드에 시계열이 저장돼 있어도(타임머신) 그래프가 그 저장소를 쓰지 않았고, /series는 원시 2000행 제한이라 3개월 조회가 불가했습니다.

이 PR은 그래프를 BE 시계열 롤업 기반으로 승격해, 브라우저를 잠깐만 켜도 최대 3개월 추세를 확대/축소하며 분석할 수 있게 합니다.

핵심 아이디어 — 티어드 롤업 + 자동 해상도

원시는 3일만 고화질로 두고, 오래된 건 숫자만 뽑은 5분 요약으로 압축. 조회 시 서버가 시간 범위에 맞춰 해상도를 자동 선택.

Backend

timemachine_store.py — 롤업 티어

  • timemachine_rollup 테이블: monitor 샘플을 5분 버킷 숫자 컬럼(sum/min/max/cnt)으로 사전 집계. payload BLOB 압축해제를 조회마다가 아니라 롤업 시 1회만.
  • run_rollup(): rowid 워터마크 기반 증분·멱등 집계(동일 ms 중복집계 불가). lock은 DB read/upsert에만, decode/aggregate는 lock 밖 → 대량 백필이 수집 스레드를 막지 않음. monitor:*만 대상(data_api는 임의 JSON이라 제외).
  • query_series_auto(): 최근(≤raw retention) 창은 raw 원시, 그 외는 5분 롤업maxPoints 이하로 GROUP BY 재집계 → 확대 시 재조회로 더 촘촘한 해상도.
  • extract_metrics(): server_resource(cpu/mem/disk:mount), network(responseTimeMs/success), http_status(responseTimeMs/up).
  • prune_rollup_older_than() + 롤업 자체 보존(기본 120일).

service.py — 파이프라인 연결

  • 기존 30분 retention 스윕에 편승(새 스레드 없음): rollup → rollup prune → raw prune. raw 보존 3일(기본 72h) 유지 권장.

routes/timemachine_routes.py

  • GET /dashboard/timemachine/series/agg{ resolution, bucketMs, series: {metric:[{ts,avg,min,max,count}]} }.

Frontend

ServerDetailPopup.jsx — 줌 가능한 추세 그래프

  • 데이터 소스 메모리 → timemachineService.queryRangeAgg로 교체(스냅샷 모드 서버).
  • 범위 프리셋(1시간~3개월) + recharts Brush 팬/줌 + 해상도 표시.
  • avg 선 + min/max 밴드 → 집계해도 순간 스파이크가 사라지지 않음.
  • 레거시(자격증명 내장) 서버는 기존 메모리 history로 폴백.

용량·성능 (현재 수집량 기준)

항목 원시 90일 롤업
디스크 ~3.3 GB ~150 MB
3개월 1회 조회 100+ MB 수십 KB
렌더 포인트 259,200 ~2,160

검증

  • py_compile 3파일 통과.
  • 실제 timemachine.db(30h)로 롤업 검증: 62,682 monitor 행 집계, 집계 수학이 raw 재계산과 정확히 일치, 자동 해상도(3개월→130분/3시간→5분), 워터마크 멱등성(재실행 0행·중복 없음), prune 동작 — 전 항목 통과.
  • BE→FE 계약 검증 8/8: 실제 series → buildRowsFromSeries 변환, avg가 모든 점에서 min/max 밴드 내 위치 등.
  • vite build 통과.
  • ⚠️ 라이브 브라우저 E2E는 원격 MariaDB(LAN) 의존으로 미수행 — 리뷰어 육안 확인 권장(추세 그래프 더블클릭 → 3개월 선택 → 확대/축소).

참고

🤖 Generated with Claude Code

https://claude.ai/code/session_01STHNiPFvtoxMbemirfk1aU

더블클릭 추세 그래프가 브라우저 메모리(historyRef)만 그려서 새로고침 때마다
초기화되고 최대 120점만 보이던 한계를, BE 시계열 저장소 기반으로 승격.

## Backend — 티어드 롤업 (timemachine_store.py)
- timemachine_rollup 테이블: monitor 샘플을 5분 버킷 숫자 컬럼(sum/min/max/cnt)
  으로 사전 집계. payload BLOB 압축해제를 조회마다가 아니라 롤업 시 1회만 수행.
- run_rollup(): rowid 워터마크 기반 증분·멱등 집계 (동일 ms 중복집계 없음).
  lock 은 DB read/upsert 시에만 잡고 decode/aggregate 는 lock 밖에서 → 대량
  백필이 수집 스레드를 막지 않음. monitor:* 만 대상 (data_api 는 임의 JSON).
- query_series_auto(): 최근(≤raw retention) 창은 raw 원시, 그 외는 5분 롤업을
  maxPoints 이하로 재집계(GROUP BY). 확대 시 재조회로 더 촘촘한 해상도.
- extract_metrics(): server_resource(cpu/mem/disk:mount), network(responseTimeMs/
  success), http_status(responseTimeMs/up) 숫자 지표 추출.
- prune_rollup_older_than() + 롤업 자체 보존(기본 120일).

## Backend — 파이프라인 연결
- service.py 30분 retention 스윕에 편승: rollup → rollup prune → raw prune
  (새 스레드 없음). raw 보존은 3일(기본 72h) 유지 권장.
- routes: GET /dashboard/timemachine/series/agg (sourceType/sourceId/from/to/
  maxPoints/metric) → { resolution, bucketMs, series: {metric:[{ts,avg,min,max,count}]} }.

## Frontend — 줌 가능한 추세 그래프 (ServerDetailPopup)
- 데이터 소스를 메모리 → timemachineService.queryRangeAgg 로 교체 (스냅샷 모드
  서버). 브라우저를 잠깐만 켜도 저장된 과거 추세 즉시 로드.
- 범위 프리셋(1시간~3개월) + recharts Brush 팬/줌 + 해상도 표시.
- avg 선 + min/max 밴드 → 집계해도 순간 스파이크가 사라지지 않음.
- 레거시(자격증명 내장) 서버는 기존 메모리 history 로 폴백.

## 검증
- py_compile 3파일 통과.
- 실제 timemachine.db(30h) 로 롤업 검증: 62,682 monitor 행 집계, 집계 수학이
  raw 재계산과 정확히 일치, 자동 해상도(3개월→130분/3시간→5분), 워터마크
  멱등성, prune 동작 — 전 항목 통과.
- 실제 series → FE buildRowsFromSeries 계약 검증 8/8 (avg 밴드 내 위치 등).
- vite build 통과.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01STHNiPFvtoxMbemirfk1aU
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant