signal-batch/docs/implementation-progress.md
HeungTak Lee b4221c36fd feat: 다중 폴리곤 영역 탐색 REST API + STRtree 공간 인덱스 (Phase 8)
캐시 기반 인메모리 다중 폴리곤 영역 내 선박 탐색 API 구현.
JTS STRtree + PreparedGeometry로 고속 공간 검색, ANY/ALL/SEQUENTIAL 3가지 모드 지원.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-07 08:38:18 +09:00

11 KiB
Raw Blame 히스토리

WebSocket 부하 제어 개선 — 구현 진행 상황

브랜치: feat/websocket-load-control 시작일: 2026-02-06


DB 커넥션 풀 분배 설계 (총 250개)

DataSource AS-IS TO-BE 비고
Query 60 (min 10) 120 (min 20) WebSocket 스트리밍 + REST API 주 사용
Collect 20 (min 5) 80 (min 15) 배치 Reader, 신호 수집 조회
Batch 20 (min 10) 30 (min 5) Spring Batch 메타데이터
예비 - 20 운영 여유분
합계 100 250 DB 서버 500 중 250 사용

글로벌 동시 쿼리 제한 산정

  • Query 풀 120개 / 쿼리당 평균 3커넥션 = 40
  • 보수적 적용: 30개 (REST API, 헬스체크 등에 여유분 확보)

Phase 1 — 긴급 안정화

  • 1.1 글로벌 동시 쿼리 제한 (Semaphore + Fair Queue)

    • ActiveQueryManager에 Fair Semaphore 기반 글로벌 동시 쿼리 제한 추가 (기본 30개)
    • @Async 메서드 내에서 슬롯 획득 (인바운드 채널 블로킹 방지 설계)
    • application-prod.yml에 websocket.query 설정 외부화
    • 상태: 완료 (2026-02-06)
  • 1.2 쿼리 완료 시 리소스 반환 보장

    • ChunkedTrackStreamingService finally 블록에 releaseQuery() + releaseQuerySlot() 추가
    • StompTrackStreamingService finally 블록에 releaseQuery() + releaseQuerySlot() 추가
    • 세션별 쿼리 카운트 감소 누락 수정
    • 상태: 완료 (2026-02-06)
  • 1.3 CachedThreadPool → 제한된 ThreadPoolExecutor 교체

    • CancellableQueryManager: newCachedThreadPool → ThreadPoolExecutor(core:5, max:20, queue:100)
    • CallerRunsPolicy로 큐 포화 시 자연 백프레셔
    • 상태: 완료 (2026-02-06)
  • 1.4 DB 커넥션 풀 재분배 (prod)

    • Query: 60→120(min 20), Collect: 20→80(min 15), Batch: 20→30(min 5)
    • 총 230/250, 예비 20개
    • 상태: 완료 (2026-02-06)

Phase 2 — 취소 및 정리 로직 완성

  • 2.1 ChunkedTrackStreamingService 쿼리 취소 구현

    • queryCancelFlags(ConcurrentHashMap<String, AtomicBoolean>) 추가
    • streamChunkedTracks 시작 시 등록, 전략별 루프 전 확인, finally에서 정리
    • cancelQuery()에 실제 플래그 설정 로직 구현 (기존 TODO 해소)
    • isQueryCancelled()에 취소 플래그 통합 확인
    • 상태: 완료 (2026-02-06)
  • 2.2 쿼리 관리 시스템 통합

    • StompTrackController의 중복 completeQuery() 제거
    • 리소스 정리를 서비스 finally 블록에서 일괄 처리하도록 단일화
    • 상태: 완료 (2026-02-06)

Phase 3 — 백프레셔 고도화

  • 3.1 콜백 기반 버퍼 추적

    • CompletableFuture+Thread.sleep(100) → try-finally 즉시 감소로 전환
    • 상태: 완료 (2026-02-06)
  • 3.2 적응형 전송 지연

    • ChunkedTrack: 버퍼 사용률(%) 기반 4단계 적응형 지연 (10~200ms)
    • StompTrack: 큐 사용률 + 데이터 크기 복합 적응형 지연
    • 상태: 완료 (2026-02-06)

Phase 4 — 설정 외부화 및 모니터링

  • 4.1 WebSocketProperties 설정 클래스

    • @ConfigurationProperties(prefix = "websocket")로 query/transport/backpressure 설정 바인딩
    • 상태: 완료 (2026-02-06)
  • 4.2 모니터링 엔드포인트

    • GET /api/websocket/load-control — 글로벌 동시 제한, 대기 큐, 활성 쿼리 상세, 메모리
    • 상태: 완료 (2026-02-06)

Phase 5 — 대기열 기반 쿼리 관리 + 타임아웃 최적화

  • 5.1 QueryStatusUpdate에 queuePosition, totalInQueue 필드 추가

    • QUEUED 상태에서 대기열 순번 정보 전달
    • 상태: 완료 (2026-02-06)
  • 5.2 ActiveQueryManager 대기열 추적 구현

    • ConcurrentLinkedQueue 기반 대기열 추적
    • tryAcquireQuerySlotImmediate(): 즉시 슬롯 획득
    • waitForSlotWithQueue(): 2초 간격 슬롯 대기
    • getQueuePosition(), getQueueSize(): 순번 조회
    • 상태: 완료 (2026-02-06)
  • 5.3 ChunkedTrackStreamingService 거부 → 대기열 전환

    • 즉시 슬롯 획득 실패 → QUEUED 상태 2초 간격 전송하며 최대 2분 대기
    • 대기 중에도 순번/전체 큐 크기 안내
    • 상태: 완료 (2026-02-06)
  • 5.4 StompTrackStreamingService 동일 패턴 적용

    • 상태: 완료 (2026-02-06)
  • 5.5 WebSocketProperties에 SessionProperties 추가

    • idleTimeoutMs, serverHeartbeatMs, clientHeartbeatMs, sockjsDisconnectDelayMs, sendTimeLimitSeconds
    • 기본값을 하드코딩에서 설정 바인딩으로 전환
    • 상태: 완료 (2026-02-06)
  • 5.6 WebSocketStompConfig 하드코딩 제거

    • 모든 타임아웃/하트비트/풀 설정을 WebSocketProperties에서 주입
    • 상태: 완료 (2026-02-06)
  • 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)
  • 5.8 AsyncConfig 스레드 풀 확장

    • core: 15 → 40, max: 30 → 120, queue: 500 → 100, keepAlive: 40s → 30s
    • 상태: 완료 (2026-02-06)

Phase 6 — 일일 데이터 인메모리 캐시

  • 6.1 DailyTrackCacheManager 신규 구현

    • @Service + @Async 비동기 캐시 매니저
    • ApplicationReadyEvent에서 비동기 워밍업 (D-1 → D-7 순차 로드)
    • 날짜별 CompactVesselTrack 캐시 (ConcurrentHashMap)
    • 뷰포트 필터링 지원, 다중 날짜 병합 조회
    • splitQueryRange(): 요청 범위 → (캐시구간, DB구간, 오늘구간) 분리
    • refreshAfterDailyJob(): 배치 완료 후 캐시 갱신
    • 상태: 완료 (2026-02-06)
  • 6.2 DailyTrackCacheProperties 신규 구현

    • @ConfigurationProperties(prefix = "cache.daily-track")
    • enabled, retentionDays, maxMemoryGb, warmupAsync
    • 상태: 완료 (2026-02-06)
  • 6.3 DailyAggregationJobConfig에 캐시 갱신 리스너 추가

    • JobExecutionListener로 배치 완료 후 refreshAfterDailyJob() 호출
    • 상태: 완료 (2026-02-06)
  • 6.4 ChunkedTrackStreamingService 캐시 통합

    • processDailyStrategy: 날짜별 캐시 히트 체크 → 메모리 조회 / DB 폴백
    • processQueryInChunks: 동일 캐시 우선 패턴
    • 상태: 완료 (2026-02-06)
  • 6.5 StompTrackStreamingService 캐시 통합

    • Daily 전략 처리 시 날짜별 캐시 히트 → 메모리 조회, 미스만 DB 처리
    • 상태: 완료 (2026-02-06)
  • 6.6 application-prod.yml 캐시 설정 추가

    • cache.daily-track: enabled=true, retention-days=7, max-memory-gb=5, warmup-async=true
    • 상태: 완료 (2026-02-06)
  • 6.7 WebSocketMonitoringController 캐시 모니터링 엔드포인트

    • GET /api/websocket/daily-cache — 캐시 상태, 날짜별 선박수/메모리, 로드 시각
    • 상태: 완료 (2026-02-06)

Phase 7 — 통합선박(IntegrationVessel) 전용 DataSource

  • 7.1 IntegrationVesselService 전용 DataSource 지원

    • vessel.integration.datasource.* 설정으로 별도 DB 연결 가능
    • @PostConstruct에서 전용 HikariDataSource 생성 (max 3, min 1)
    • 미설정 시 queryDataSource 자동 폴백 (기존 동작 유지)
    • @PreDestroy에서 전용 DataSource 정리
    • 상태: 완료 (2026-02-07)
  • 7.2 설정 기반 테이블명

    • vessel.integration.table-name 프로파일별 분리
    • prod: t_ship_integration_sub (gis 스키마는 jdbc-url currentSchema)
    • dev/prod-mpr/local/query: signal.t_ship_integration_sub (기본값)
    • 상태: 완료 (2026-02-07)
  • 7.3 프로파일 호환성 유지

    • prod: 별도 DB (10.188.141.230:5432/mdadb2 gis) → 전용 DataSource
    • dev/prod-mpr: queryDB signal 스키마 → queryDataSource 폴백
    • local/query: integration 비활성화 (기본값)
    • 기존 DataSourceConfig 4개 파일 수정 없음
    • 상태: 완료 (2026-02-07)

Phase 8 — 다중 폴리곤 영역 탐색 REST API + 공간 인덱스

  • 8.1 DailyTrackCacheManager에 STRtree 공간 인덱스 추가

    • DailyTrackData에 STRtree spatialIndex 필드 추가 (하위 호환 유지)
    • loadDay() 완료 후 자동 빌드 → build() 호출로 불변화 (동시성 안전)
    • getDailyTrackData(date), getCachedDateList() public 메서드 추가
    • 추가 메모리: 선박당 ~100B × 50K/일 = ~5MB/일, 7일 ~35MB
    • 상태: 완료 (2026-02-07)
  • 8.2 AreaSearchRequest / AreaSearchResponse DTO 생성

    • AreaSearchRequest: startTime/endTime + SearchMode(ANY/ALL/SEQUENTIAL) + List
    • AreaSearchResponse: tracks(CompactVesselTrack[]) + hitDetails + summary
    • Swagger @Schema 상세 기입, 요청/응답 예시 포함
    • 상태: 완료 (2026-02-07)
  • 8.3 AreaSearchService 핵심 비즈니스 로직 구현

    • JTS PreparedGeometry + STRtree 기반 고속 영역 탐색
    • 다일 데이터 병합 → 단일 STRtree → 폴리곤별 후보 추출 → 정밀 PIP
    • ANY(합집합)/ALL(교집합)/SEQUENTIAL(순차 통과) 3가지 모드
    • 캐시 미준비 시 CacheNotReadyException → 503 반환
    • 상태: 완료 (2026-02-07)
  • 8.4 AreaSearchController REST 엔드포인트

    • POST /api/v2/tracks/area-search
    • @Tag("항적 조회 API V2") → 기존 GisControllerV2와 동일 Swagger 그룹
    • 에러 핸들러: 400 (잘못된 폴리곤), 503 (캐시 미준비)
    • Swagger: @Operation, @ExampleObject 상세 문서화
    • 상태: 완료 (2026-02-07)

커밋 이력

날짜 Phase 커밋 메시지 해시
2026-02-06 1.1+1.2 feat: 글로벌 동시 쿼리 제한(Semaphore) 및 리소스 반환 보장 78ff307
2026-02-06 1.3 fix: CachedThreadPool → 제한된 ThreadPoolExecutor 교체 2191671
2026-02-06 1.4 perf: DB 커넥션 풀 재분배 (총 250개, prod) 122a247
2026-02-06 2.1 feat: ChunkedTrackStreamingService 쿼리 취소 로직 구현 e073007
2026-02-06 2.2 refactor: 쿼리 생명주기 관리 서비스 finally 블록으로 단일화 28908e1
2026-02-06 3 perf: 백프레셔 고도화 - 정확한 버퍼 추적 및 적응형 지연 7b7e283
2026-02-06 4 feat: WebSocket 설정 외부화 및 부하 제어 모니터링 엔드포인트 c92bf0e
2026-02-06 5 feat: 대기열 기반 쿼리 관리 및 타임아웃 최적화 7bd7bf5
2026-02-06 6 feat: 일일 데이터 인메모리 캐시 구현 03b14e6
2026-02-06 5~6 docs: Phase 5~6 구현 진행 문서 및 성능 보고서 업데이트 dc586dd
2026-02-06 6 fix: 캐시-DB 하이브리드 조회 시 뷰포트 2-pass 필터링 정합성 보장 e9d5d36