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:
부모
03b14e687a
커밋
dc586dde0c
@ -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 | 커밋 메시지 | 해시 |
|
| 날짜 | Phase | 커밋 메시지 | 해시 |
|
||||||
|
|||||||
@ -849,17 +849,130 @@ maxConcurrentGlobal = min(DB커넥션풀 / 2, Async스레드풀max)
|
|||||||
| `domain/vessel/service/optimization/TrackStreamingOptimizer.java` | 적응형 최적화 (미사용) |
|
| `domain/vessel/service/optimization/TrackStreamingOptimizer.java` | 적응형 최적화 (미사용) |
|
||||||
| `monitoring/service/TrackStreamingMetrics.java` | 스트리밍 메트릭 |
|
| `monitoring/service/TrackStreamingMetrics.java` | 스트리밍 메트릭 |
|
||||||
|
|
||||||
## 부록 B. 운영 환경 리소스 현황
|
## Phase 5. 대기열 기반 쿼리 관리 + 타임아웃 최적화
|
||||||
|
|
||||||
| 리소스 | 설정값 | 비고 |
|
### 5.1 대기열 구조
|
||||||
|--------|--------|------|
|
|
||||||
| JVM Heap | 8~16GB (서버 메모리의 1/3) | G1GC |
|
기존: 슬롯 부족 시 즉시 거부 (`ERROR: Server is busy`)
|
||||||
| Tomcat Threads | max 200 | |
|
개선: 대기열 순번 안내 후 최대 2분 대기 (`QUEUED: position 3/5`)
|
||||||
| 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 | |
|
tryAcquireSlotImmediate()
|
||||||
| STOMP Outbound | core 20, max 40, queue 5000 | |
|
│
|
||||||
| WebSocket Buffer | send 256MB, message 50MB | |
|
├── 성공 → 즉시 처리
|
||||||
|
│
|
||||||
|
└── 실패 → 대기열 진입
|
||||||
|
│ ┌──────────────────────────────┐
|
||||||
|
│ │ 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** | 비동기 워밍업 |
|
||||||
|
|||||||
불러오는 중...
Reference in New Issue
Block a user