DOCS/troubleshooting/250715_metrics_graph_timebucket_error.md

4.0 KiB

시스템 메트릭 그래프 time_bucket 쿼리 오류 해결

개요

  • 날짜: 2025-07-15
  • 문제: 관리자 대시보드에서 시스템 메트릭 그래프가 표시되지 않음
  • 원인: TimescaleDB time_bucket 함수의 asyncpg 파라미터 바인딩 문제

문제 상황

증상

  • 관리자 대시보드의 시스템 메트릭 그래프에 데이터가 표시되지 않음
  • PostgreSQL에는 336개의 메트릭 데이터가 정상적으로 저장됨
  • 백엔드 로그에서 쿼리 오류 발생

에러 메시지

메트릭 데이터 조회 실패 (1h): invalid input for query argument $1: '1 minute' ('str' object has no attribute 'days')

원인 분석

코드 문제점

# 문제가 있던 코드
query = """
SELECT 
    time_bucket($1, time) as time_bucket,
    metric_type,
    AVG(value) as avg_value
FROM system_metrics 
WHERE time >= $2 AND metric_type IN (...)
GROUP BY time_bucket, metric_type
ORDER BY time_bucket, metric_type
"""

rows = await conn.fetch(query, interval, start_time)

문제 원인

  1. asyncpg vs psycopg2 차이: asyncpg는 파라미터 바인딩에서 PostgreSQL interval 타입 자동 변환을 지원하지 않음
  2. TimescaleDB time_bucket 함수: time_bucket('1 minute', time) 형식이 필요한데 문자열 '1 minute'$1 파라미터로 전달할 수 없음
  3. 타입 불일치: 문자열을 interval 타입으로 변환하는 과정에서 오류 발생

해결 방법

적용한 해결책: f-string 직접 삽입

# 수정된 코드
query = f"""
SELECT 
    time_bucket('{interval}', time) as time_bucket,
    metric_type,
    AVG(value) as avg_value
FROM system_metrics 
WHERE time >= $1 AND metric_type IN (...)
GROUP BY time_bucket, metric_type
ORDER BY time_bucket, metric_type
"""

rows = await conn.fetch(query, start_time)

다른 가능한 해결 방법들

  1. 명시적 타입 캐스팅
query = """
SELECT time_bucket($1::interval, time) as time_bucket, ...
"""
rows = await conn.fetch(query, interval, start_time)
  1. SQL 함수 사용
query = """
SELECT time_bucket(INTERVAL %s, time) as time_bucket, ...
"""
# 하지만 asyncpg는 %s 문법을 지원하지 않음

검증 과정

1. PostgreSQL에서 직접 테스트

sudo -u postgres psql robeing_metrics -c "SELECT time_bucket('1 minute', NOW()) as test_bucket;"
# 결과: 정상 동작 확인

2. 데이터 존재 확인

sudo -u postgres psql robeing_metrics -c "SELECT COUNT(*) FROM system_metrics;"
# 결과: 336개 데이터 존재

3. 최신 데이터 확인

sudo -u postgres psql robeing_metrics -c "SELECT time, metric_type, value FROM system_metrics ORDER BY time DESC LIMIT 5;"
# 결과: 1분 전까지 정상 수집됨

적용 결과

수정 후 기대되는 결과:

  • 관리자 대시보드에서 시스템 메트릭 그래프 정상 표시
  • 1h/1d/7d/30d/90d/1y 기간별 데이터 조회 정상화
  • Chart.js 그래프에 실제 데이터 렌더링

주의사항

SQL Injection 방지

f-string 사용 시 주의할 점:

  • interval 값은 사전 정의된 값들만 사용 ('1 minute', '1 hour' 등)
  • 사용자 입력을 직접 f-string에 넣지 않음
  • 화이트리스트 방식으로 검증:
time_ranges = {
    '1h': (timedelta(hours=1), '1 minute'),
    '1d': (timedelta(days=1), '10 minutes'),
    '7d': (timedelta(days=7), '1 hour'),
    '30d': (timedelta(days=30), '4 hours'),
    '90d': (timedelta(days=90), '12 hours'),
    '1y': (timedelta(days=365), '1 day')
}

성능 고려사항

  • time_bucket 함수는 TimescaleDB에서 최적화됨
  • 인덱스가 적절히 설정되어 있어 성능 문제 없음
  • 대용량 데이터에서도 효율적인 집계 가능

참고 자료