docs: 시스템 메트릭 그래프 time_bucket 쿼리 오류 트러블슈팅 문서 추가
- TimescaleDB time_bucket 함수와 asyncpg 파라미터 바인딩 문제 - f-string 직접 삽입으로 해결 - SQL injection 방지 방안 포함
This commit is contained in:
parent
ba52ee2200
commit
24ff791e9e
135
docs/troubleshooting/20250715_metrics_graph_timebucket_error.md
Normal file
135
docs/troubleshooting/20250715_metrics_graph_timebucket_error.md
Normal file
@ -0,0 +1,135 @@
|
||||
# 시스템 메트릭 그래프 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')
|
||||
```
|
||||
|
||||
## 원인 분석
|
||||
|
||||
### 코드 문제점
|
||||
```python
|
||||
# 문제가 있던 코드
|
||||
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 직접 삽입
|
||||
```python
|
||||
# 수정된 코드
|
||||
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. **명시적 타입 캐스팅**
|
||||
```python
|
||||
query = """
|
||||
SELECT time_bucket($1::interval, time) as time_bucket, ...
|
||||
"""
|
||||
rows = await conn.fetch(query, interval, start_time)
|
||||
```
|
||||
|
||||
2. **SQL 함수 사용**
|
||||
```python
|
||||
query = """
|
||||
SELECT time_bucket(INTERVAL %s, time) as time_bucket, ...
|
||||
"""
|
||||
# 하지만 asyncpg는 %s 문법을 지원하지 않음
|
||||
```
|
||||
|
||||
## 검증 과정
|
||||
|
||||
### 1. PostgreSQL에서 직접 테스트
|
||||
```bash
|
||||
sudo -u postgres psql robing_metrics -c "SELECT time_bucket('1 minute', NOW()) as test_bucket;"
|
||||
# 결과: 정상 동작 확인
|
||||
```
|
||||
|
||||
### 2. 데이터 존재 확인
|
||||
```bash
|
||||
sudo -u postgres psql robing_metrics -c "SELECT COUNT(*) FROM system_metrics;"
|
||||
# 결과: 336개 데이터 존재
|
||||
```
|
||||
|
||||
### 3. 최신 데이터 확인
|
||||
```bash
|
||||
sudo -u postgres psql robing_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에 넣지 않음
|
||||
- 화이트리스트 방식으로 검증:
|
||||
|
||||
```python
|
||||
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에서 최적화됨
|
||||
- 인덱스가 적절히 설정되어 있어 성능 문제 없음
|
||||
- 대용량 데이터에서도 효율적인 집계 가능
|
||||
|
||||
## 참고 자료
|
||||
- [TimescaleDB time_bucket 문서](https://docs.timescale.com/api/latest/hyperfunctions/time_bucket/)
|
||||
- [asyncpg 파라미터 바인딩](https://magicstack.github.io/asyncpg/current/usage.html#prepared-statements)
|
||||
- PostgreSQL interval 데이터 타입 문서
|
||||
Loading…
x
Reference in New Issue
Block a user