docs: Phase 5~6 구현 진행 문서 및 성능 보고서 업데이트

- implementation-progress.md: Phase 5~6 체크리스트 추가 (전항목 완료)
- websocket-performance-improvement-report.md: 대기열 구조, 캐시 아키텍처 설명 추가

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
HeungTak Lee 2026-02-06 15:34:32 +09:00
부모 03b14e687a
커밋 dc586dde0c
2개의 변경된 파일207개의 추가작업 그리고 13개의 파일을 삭제

파일 보기

@ -88,6 +88,87 @@
---
## Phase 5 — 대기열 기반 쿼리 관리 + 타임아웃 최적화
- [x] **5.1** QueryStatusUpdate에 `queuePosition`, `totalInQueue` 필드 추가
- QUEUED 상태에서 대기열 순번 정보 전달
- 상태: **완료** (2026-02-06)
- [x] **5.2** ActiveQueryManager 대기열 추적 구현
- ConcurrentLinkedQueue 기반 대기열 추적
- `tryAcquireQuerySlotImmediate()`: 즉시 슬롯 획득
- `waitForSlotWithQueue()`: 2초 간격 슬롯 대기
- `getQueuePosition()`, `getQueueSize()`: 순번 조회
- 상태: **완료** (2026-02-06)
- [x] **5.3** ChunkedTrackStreamingService 거부 → 대기열 전환
- 즉시 슬롯 획득 실패 → QUEUED 상태 2초 간격 전송하며 최대 2분 대기
- 대기 중에도 순번/전체 큐 크기 안내
- 상태: **완료** (2026-02-06)
- [x] **5.4** StompTrackStreamingService 동일 패턴 적용
- 상태: **완료** (2026-02-06)
- [x] **5.5** WebSocketProperties에 SessionProperties 추가
- idleTimeoutMs, serverHeartbeatMs, clientHeartbeatMs, sockjsDisconnectDelayMs, sendTimeLimitSeconds
- 기본값을 하드코딩에서 설정 바인딩으로 전환
- 상태: **완료** (2026-02-06)
- [x] **5.6** WebSocketStompConfig 하드코딩 제거
- 모든 타임아웃/하트비트/풀 설정을 WebSocketProperties에서 주입
- 상태: **완료** (2026-02-06)
- [x] **5.7** application-prod.yml 설정 변경
- Query 풀: 120 → 180, global: 30 → 60, max-per-session: 3 → 20
- Session idle: 60s → 15s, Heartbeat: 10s → 5s, SockJS disconnect: 30s → 5s
- Send time limit: 120s → 30s
- 상태: **완료** (2026-02-06)
- [x] **5.8** AsyncConfig 스레드 풀 확장
- core: 15 → 40, max: 30 → 120, queue: 500 → 100, keepAlive: 40s → 30s
- 상태: **완료** (2026-02-06)
---
## Phase 6 — 일일 데이터 인메모리 캐시
- [x] **6.1** DailyTrackCacheManager 신규 구현
- @Service + @Async 비동기 캐시 매니저
- ApplicationReadyEvent에서 비동기 워밍업 (D-1 → D-7 순차 로드)
- 날짜별 CompactVesselTrack 캐시 (ConcurrentHashMap)
- 뷰포트 필터링 지원, 다중 날짜 병합 조회
- splitQueryRange(): 요청 범위 → (캐시구간, DB구간, 오늘구간) 분리
- refreshAfterDailyJob(): 배치 완료 후 캐시 갱신
- 상태: **완료** (2026-02-06)
- [x] **6.2** DailyTrackCacheProperties 신규 구현
- @ConfigurationProperties(prefix = "cache.daily-track")
- enabled, retentionDays, maxMemoryGb, warmupAsync
- 상태: **완료** (2026-02-06)
- [x] **6.3** DailyAggregationJobConfig에 캐시 갱신 리스너 추가
- JobExecutionListener로 배치 완료 후 refreshAfterDailyJob() 호출
- 상태: **완료** (2026-02-06)
- [x] **6.4** ChunkedTrackStreamingService 캐시 통합
- processDailyStrategy: 날짜별 캐시 히트 체크 → 메모리 조회 / DB 폴백
- processQueryInChunks: 동일 캐시 우선 패턴
- 상태: **완료** (2026-02-06)
- [x] **6.5** StompTrackStreamingService 캐시 통합
- Daily 전략 처리 시 날짜별 캐시 히트 → 메모리 조회, 미스만 DB 처리
- 상태: **완료** (2026-02-06)
- [x] **6.6** application-prod.yml 캐시 설정 추가
- cache.daily-track: enabled=true, retention-days=7, max-memory-gb=5, warmup-async=true
- 상태: **완료** (2026-02-06)
- [x] **6.7** WebSocketMonitoringController 캐시 모니터링 엔드포인트
- GET /api/websocket/daily-cache — 캐시 상태, 날짜별 선박수/메모리, 로드 시각
- 상태: **완료** (2026-02-06)
---
## 커밋 이력
| 날짜 | Phase | 커밋 메시지 | 해시 |

파일 보기

@ -849,17 +849,130 @@ maxConcurrentGlobal = min(DB커넥션풀 / 2, Async스레드풀max)
| `domain/vessel/service/optimization/TrackStreamingOptimizer.java` | 적응형 최적화 (미사용) |
| `monitoring/service/TrackStreamingMetrics.java` | 스트리밍 메트릭 |
## 부록 B. 운영 환경 리소스 현황
## Phase 5. 대기열 기반 쿼리 관리 + 타임아웃 최적화
| 리소스 | 설정값 | 비고 |
|--------|--------|------|
| JVM Heap | 8~16GB (서버 메모리의 1/3) | G1GC |
| Tomcat Threads | max 200 | |
| Tomcat Connections | max 10,000 | |
| DB Pool (Query) | max 60 | 주 사용 |
| DB Pool (Collect) | max 20 | 읽기 전용 |
| DB Pool (Batch) | max 20 | 메타데이터 |
| trackStreamingExecutor | core 15, max 30, queue 500 | CallerRunsPolicy |
| STOMP Inbound | core 10, max 20, queue 100 | |
| STOMP Outbound | core 20, max 40, queue 5000 | |
| WebSocket Buffer | send 256MB, message 50MB | |
### 5.1 대기열 구조
기존: 슬롯 부족 시 즉시 거부 (`ERROR: Server is busy`)
개선: 대기열 순번 안내 후 최대 2분 대기 (`QUEUED: position 3/5`)
```
요청 유입
tryAcquireSlotImmediate()
├── 성공 → 즉시 처리
└── 실패 → 대기열 진입
│ ┌──────────────────────────────┐
│ │ 2초 간격 루프: │
│ │ 1. tryAcquire (2초 타임아웃) │
│ │ 2. QUEUED 상태 전송 │
│ │ (순번/전체 큐 크기) │
│ │ 3. 최대 2분까지 반복 │
│ └──────────────────────────────┘
├── 슬롯 획득 → 처리 시작
└── 타임아웃 → ERROR 응답
```
### 5.2 타임아웃 최적화
| 설정 | AS-IS | TO-BE | 근거 |
|------|-------|-------|------|
| Query DB 풀 | 120 | **180** | 동시 60쿼리 × 3커넥션 |
| max-concurrent-global | 30 | **60** | 180 / ~3 |
| max-per-session | 3 | **20** | 대기열 방식이므로 넉넉하게 |
| Executor max | 30 | **120** | 60실행 + 60대기 |
| Session idle timeout | 60s | **15s** | 빠른 정리 |
| Heartbeat | 10s/10s | **5s/5s** | 죽은 연결 빠른 감지 |
| SockJS disconnect delay | 30s | **5s** | 빠른 해제 |
| Send time limit | 120s | **30s** | 응답 완료 후 빠른 정리 |
---
## Phase 6. 일일 데이터 인메모리 캐시
### 6.1 캐시 아키텍처
일일(Daily) 집계 테이블의 7일분 데이터를 메모리에 캐시하여 DB 조회를 생략.
```
DailyTrackCacheManager (@Service)
├── cache: ConcurrentHashMap<LocalDate, DailyTrackData>
│ ├── D-7 → {tracks: Map<vesselId, CompactVesselTrack>, vesselCount, memorySizeBytes}
│ ├── D-6 → ...
│ ├── ...
│ └── D-1 → ...
├── 비동기 워밍업 (ApplicationReadyEvent → @Async)
│ → 스케줄러/API/WebSocket 차단 없이 백그라운드 로드
│ → D-1 → D-2 → ... → D-7 (최근 우선)
│ → 각 날짜 로드 완료 즉시 캐시 활성화
├── 하이브리드 쿼리 분리
│ → 요청 범위의 모든 날짜를 캐시 확인
│ → 히트: 메모리 조회, 미스: DB 조회, 오늘: hourly/5min 테이블
│ → 결과 vessel 기준 병합
└── 일일 갱신 (DailyAggregationJob 완료 시)
→ D-1 (재)로드 + D-8 제거
```
### 6.2 캐시 용량 추정
| 항목 | 수치 |
|------|------|
| Daily 테이블 1일분 | ~350MB (DB) |
| 7일분 인메모리 추정 | ~4GB (Java 객체 오버헤드 포함) |
| 최대 메모리 한도 | 5GB (설정 가능) |
| JVM 힙 (권장) | 12GB 이상 |
### 6.3 쿼리 라우팅
```
요청: 2026-01-30 ~ 2026-02-06 15:00
1. 날짜별 분류:
├─ 02-06 (오늘) → DB 필수 (hourly/5min 테이블)
├─ 02-05: cache HIT → 메모리
├─ 02-04: cache HIT → 메모리
├─ ...
├─ 01-31: cache HIT → 메모리
└─ 01-30: cache MISS → DB (daily 테이블)
2. 결과: vessel 기준 병합 → 기존 응답 구조 그대로
```
### 6.4 모니터링
```
GET /api/websocket/daily-cache
{
"status": "READY",
"cachedDays": 7,
"totalVessels": 280000,
"totalMemoryMb": 3500,
"days": [
{"date": "2026-02-05", "vesselCount": 42000, "memorySizeMb": 520, "loadedAt": "..."},
...
]
}
```
---
## 부록 B. 운영 환경 리소스 현황 (Phase 5~6 반영)
| 리소스 | AS-IS | TO-BE | 비고 |
|--------|-------|-------|------|
| JVM Heap | 8~16GB | **12~16GB 권장** | 캐시 ~4GB + 운영 |
| DB Pool (Query) | max 120 | **max 180** | WebSocket + REST |
| DB Pool (Collect) | max 80 | max 80 | 배치 Reader |
| DB Pool (Batch) | max 30 | max 30 | 메타데이터 |
| max-concurrent-global | 30 | **60** | Query풀 180 / 3 |
| trackStreamingExecutor | core 15, max 30 | **core 40, max 120** | 대기열 + 실행 |
| Session idle timeout | 60s | **15s** | 빠른 정리 |
| Heartbeat | 10s/10s | **5s/5s** | 빠른 감지 |
| Daily cache | - | **7일분 ~4GB** | 비동기 워밍업 |