# API 통계 수집 메모리 사용량 예측

## 개요
API 통계 수집 시스템은 분단위/시간별로 메모리에서 집계 후 DB에 저장합니다.
이 문서는 메모리 사용량을 예측하고, 안전한 운영을 위한 제한값을 설명합니다.

## Aggregator 메모리 구성

### 1. Key 메모리 (MinuteStatsKey / HourStatsKey)

| 필드 | 타입 | 평균 크기 | 메모리 |
|------|------|----------|--------|
| statTime | LocalDateTime | - | 24 bytes |
| apiName | String | 50자 | ~124 bytes |
| gwInstanceId | String | 20자 | ~64 bytes |
| bizDivCode | String | 10자 | ~44 bytes |
| clientId | String | 20자 | ~64 bytes |
| inboundAdapter | String | 20자 | ~64 bytes |
| outboundAdapter | String | 20자 | ~64 bytes |
| 객체 오버헤드 | - | - | ~16 bytes |
| **합계** | | | **~464 bytes** |

### 2. Aggregator 메모리 (MinuteStatsAggregator / HourStatsAggregator)

| 구성요소 | 개수/크기 | 메모리 |
|---------|----------|--------|
| AtomicLong (건수 메트릭) | 8개 | 64 bytes |
| AtomicLong (900 에러 카운트) | 3개 | 24 bytes |
| AtomicLong[] (Bucket) | 10개 | 80 bytes |
| long (min/max/total) | 3개 | 24 bytes |
| 기타 필드 및 오버헤드 | - | ~592 bytes |
| **합계** | | **~784 bytes** |

### 3. Aggregator 1개당 총 메모리

```
Key + Aggregator = 464 + 784 = 약 1.2KB
```

## 메모리 사용량 시나리오

### 시나리오 1: 일반적인 경우

**가정:**
- API 종류: 100개
- 클라이언트: 50개
- 평균 차원 조합: 200개 (동시에 활성화된 조합)

**메모리 사용량:**
- 분단위: 200개 × 1.2KB = **240KB**
- 시간별: 200개 × 1.2KB = **240KB**
- **합계**: **약 480KB**

### 시나리오 2: 높은 부하

**가정:**
- API 종류: 500개
- 클라이언트: 200개
- 평균 차원 조합: 2,000개

**메모리 사용량:**
- 분단위: 2,000개 × 1.2KB = **2.4MB**
- 시간별: 2,000개 × 1.2KB = **2.4MB**
- **합계**: **약 4.8MB**

### 시나리오 3: 최대 제한 도달 (비정상)

**가정:**
- 분단위 aggregator: 10,000개 (최대 제한)
- 시간별 aggregator: 5,000개 (최대 제한)

**메모리 사용량:**
- 분단위: 10,000개 × 1.2KB = **12MB**
- 시간별: 5,000개 × 1.2KB = **6MB**
- **합계**: **약 18MB**

> ⚠️ **주의**: 이 시나리오는 비정상 상황입니다. 정상적으로는 스케줄러가 주기적으로 flush하여 aggregator 수를 낮게 유지합니다.

## 메모리 보호 메커니즘

### 1. 최대 Aggregator 수 제한

`ApiStatsCollector.java`:
```java
private static final int MAX_MINUTE_AGGREGATORS = 10000;
private static final int MAX_HOUR_AGGREGATORS = 5000;
```

### 2. 자동 Flush

Aggregator 개수가 제한에 도달하면:
1. WARN 로그 출력
2. 완료된 시간의 데이터 자동 flush
3. 메모리 확보

### 3. 스케줄러 주기

- **분단위**: 매 분 5초에 이전 분 데이터 flush
- **시간별**: 매 시간 5분에 이전 시간 데이터 flush

정상 작동 시 aggregator는 주로 현재 시간대 데이터만 유지합니다.

## 차원 조합 수 계산

### 차원 구조
```
차원 조합 = API × GW Instance × 업무구분 × Client × Inbound × Outbound
```

### 예시 계산

**케이스 1: 중소규모**
- API: 100개
- GW Instance: 3개
- 업무구분: 5개
- Client: 10개
- Inbound: 5개
- Outbound: 5개
- **이론적 최대**: 100 × 3 × 5 × 10 × 5 × 5 = 375,000 조합

> 💡 **실제로는 활성화된 조합만 메모리에 존재**하므로 훨씬 적습니다. (일반적으로 수백~수천 개)

**케이스 2: 대규모**
- API: 500개
- 활성 조합 비율: 1% (실제 사용되는 조합)
- **실제 조합**: 약 3,750개
- **분단위 메모리**: 3,750 × 1.2KB = **4.5MB**
- **시간별 메모리**: 3,750 × 1.2KB = **4.5MB**
- **합계**: **약 9MB**

## 모니터링 지표

### 정상 범위
- 분단위 aggregator: **100~1,000개**
- 시간별 aggregator: **100~1,000개**
- 메모리 사용량: **1~5MB**

### 경고 범위
- 분단위 aggregator: **1,000~5,000개**
- 시간별 aggregator: **1,000~3,000개**
- 메모리 사용량: **5~10MB**

### 위험 범위 (조치 필요)
- 분단위 aggregator: **5,000개 이상**
- 시간별 aggregator: **3,000개 이상**
- 메모리 사용량: **10MB 이상**

> ⚠️ 위험 범위 진입 시 차원 수를 줄이거나 제한값 조정을 검토해야 합니다.

## 메모리 최적화 효과

### Bucket 방식 적용 전후 비교 (참고)

**이전 방식의 문제점:**
- Aggregator당 약 2.6MB 사용 (HdrHistogram 포함)
- 1,000개 시: 약 2.6GB
- 10,000개 시: 약 26GB

**현재 방식:**
- Aggregator당 약 1.2KB 사용 (Bucket 배열)
- 1,000개 시: 약 1.2MB
- 10,000개 시: 약 12MB

**메모리 절감**: **99.95% 감소**

## 권장 사항

### 1. 모니터링
- `ApiStatsCollector.getActiveMinuteAggregatorCount()` 주기적 확인
- `ApiStatsCollector.getActiveHourAggregatorCount()` 주기적 확인
- WARN/ERROR 로그 모니터링

### 2. 차원 최적화
- 불필요한 차원 제거 (예: null 값 정규화)
- 고카디널리티 차원 주의 (clientId 등)

### 3. 제한값 조정
운영 환경에 따라 `ApiStatsCollector.java`의 상수 조정:
```java
private static final int MAX_MINUTE_AGGREGATORS = 10000; // 조정 가능
private static final int MAX_HOUR_AGGREGATORS = 5000;    // 조정 가능
```

### 4. 설정 관리
PropManager를 통해 동적으로 활성화/비활성화:
- `ApiStats.enabled`: 전체 ON/OFF
- `ApiStats.minute.enabled`: 분단위 ON/OFF
- `ApiStats.hour.enabled`: 시간별 ON/OFF

설정 비활성화 시 메모리의 모든 데이터가 자동으로 flush됩니다.
