server: shutdown: graceful servlet: context-path: /signal-batch spring: application: name: vessel-batch-aggregation lifecycle: timeout-per-shutdown-phase: 3m # graceful shutdown 대기 (진행 중 Job 완료) main: allow-bean-definition-overriding: true # Bean 중복 정의 허용 profiles: # active 프로파일은 실행 시 명시적으로 지정 권장 # 빌드 시: -DSPRING_PROFILES_ACTIVE=prod or -DSPRING_PROFILES_ACTIVE=query # 실행 시: --spring.profiles.active=prod or --spring.profiles.active=query # 환경변수: export SPRING_PROFILES_ACTIVE=prod active: ${SPRING_PROFILES_ACTIVE:prod} # 기본값: prod (환경변수 없을 경우) # Jackson 설정 jackson: serialization: write-dates-as-timestamps: false deserialization: fail-on-unknown-properties: false time-zone: Asia/Seoul date-format: yyyy-MM-dd'T'HH:mm:ss batch: job: enabled: false # 자동 실행 방지 jdbc: initialize-schema: always table-prefix: BATCH_ # 배치 테이블 prefix # 공통 데이터소스 설정 datasource: # 수집 DB collect: jdbc-url: ${COLLECT_DB_URL:jdbc:postgresql://localhost:5432/wingdb?stringtype=unspecified¤tSchema=signal&TimeZone=Asia/Seoul} username: ${COLLECT_DB_USER:collect_user} password: ${COLLECT_DB_PASS:collect_pass} driver-class-name: org.postgresql.Driver pool-name: CollectHikariPool connection-timeout: 30000 idle-timeout: 600000 max-lifetime: 1800000 maximum-pool-size: 20 minimum-idle: 5 # 조회 DB query: jdbc-url: ${QUERY_DB_URL:jdbc:postgresql://localhost:5432/wingdb?stringtype=unspecified¤tSchema=signal&TimeZone=Asia/Seoul} username: ${QUERY_DB_USER:query_user} password: ${QUERY_DB_PASS:query_pass} driver-class-name: org.postgresql.Driver pool-name: QueryHikariPool connection-timeout: 30000 idle-timeout: 600000 max-lifetime: 1800000 maximum-pool-size: 30 minimum-idle: 10 # 배치 메타데이터 DB batch: jdbc-url: ${BATCH_DB_URL:jdbc:postgresql://localhost:5432/wingdb?stringtype=unspecified¤tSchema=signal&TimeZone=Asia/Seoul} username: ${BATCH_DB_USER:batch_user} password: ${BATCH_DB_PASS:batch_pass} driver-class-name: org.postgresql.Driver pool-name: BatchHikariPool maximum-pool-size: 10 minimum-idle: 2 # 로깅 설정 logging: level: root: INFO gc.mda.signal_batch: INFO gc.mda.signal_batch.util: WARN com.vessel.batch: INFO org.springframework.batch: INFO org.springframework.jdbc: WARN org.postgresql: WARN pattern: console: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n" file: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n" file: name: ${LOG_PATH:logs}/vessel-batch.log logback: rollingpolicy: max-file-size: 100MB max-history: 30 # 액추에이터 설정 management: endpoints: web: exposure: include: health,info,metrics,prometheus,env,loggers,threaddump,heapdump base-path: /actuator endpoint: health: show-details: always show-components: always probes: enabled: true metrics: enabled: true metrics: export: prometheus: enabled: true step: 30s tags: application: ${spring.application.name} environment: ${spring.profiles.active} distribution: percentiles-histogram: batch.job.duration: true batch.step.duration: true batch.chunk.duration: true batch.database.operation: true percentiles: batch.job.duration: 0.5, 0.75, 0.95, 0.99 batch.step.duration: 0.5, 0.75, 0.95, 0.99 prometheus: metrics: export: enabled: true # 커스텀 설정 vessel: # 데이터 필터 설정 filter: zero-coordinates: enabled: ${FILTER_ZERO_COORDS:false} # 0 근처 좌표 필터링 (기본: 비활성화) # true일 경우 lat/lon이 -1 ~ 1 범위인 데이터를 제외 # 통합선박 설정 integration: enabled: ${INTEGRATION_ENABLED:false} # 통합선박 기능 활성화 여부 cache: refresh-cron: "0 0 6 * * ?" # 매일 06:00 갱신 datasource: jdbc-url: "" # 빈 값: queryDataSource 폴백, 별도 DB 사용 시 프로파일에서 오버라이드 username: "" password: "" table-name: signal.t_ship_integration_sub # 기본: queryDB의 signal 스키마 batch: chunk-size: ${BATCH_CHUNK_SIZE:10000} page-size: ${BATCH_PAGE_SIZE:10000} partition-size: ${BATCH_PARTITION_SIZE:24} skip-limit: 100 retry-limit: 3 # Reader 설정 use-cursor-reader: true # Cursor Reader 사용 여부 fetch-size: 50000 # 한번에 가져올 row 수 # 타임아웃 설정 query-timeout: 1800 # 30분 # 파티션 관리 설정 (queryDB만 관리) partition: # 전역 설정 future-days: 7 # 미래 파티션 생성 기간 (일) enable-auto-management: true # 파티션 자동 생성 enable-auto-cleanup: true # 파티션 자동 정리 # 기본 보관 기간 default-retention: daily-partitions-retention-days: 7 # 일별 파티션 기본 보관 (일) monthly-partitions-retention-months: 2 # 월별 파티션 기본 보관 (월) # 테이블별 보관 기간 (기본값과 다를 경우만 설정) tables: t_vessel_tracks_5min: retention-days: 7 # 7일 보관 t_area_vessel_tracks: retention-days: 30 # 30일 보관 t_area_tracks_summary: retention-days: 30 # 30일 보관 t_area_statistics: retention-days: 30 # 30일 보관 t_grid_vessel_tracks: retention-days: 14 # 14일 보관 t_grid_tracks_summary: retention-days: 14 # 14일 보관 t_tile_summary: retention-days: 7 # 7일 보관 # 월별 파티션 테이블 (단위: 월) t_vessel_tracks_hourly: retention-months: 2 # 2개월 보관 t_vessel_tracks_daily: retention-months: 60 # 60개월 보관 t_grid_tracks_summary_hourly: retention-months: 2 # 2개월 보관 t_grid_tracks_summary_daily: retention-months: 3 # 3개월 보관 t_area_tracks_summary_hourly: retention-months: 2 # 2개월 보관 t_area_tracks_summary_daily: retention-months: 3 # 3개월 보관 t_abnormal_tracks: retention-months: 0 # 무한 보관 t_chnprmship_positions: retention-months: 3 # 3개월 보관 # Bulk Insert 설정 bulk-insert: batch-size: 10000 # 한번에 처리할 레코드 수 (5분 배치에 최적화) parallel-threads: 4 # 병렬 처리 스레드 수 use-binary-copy: false # 바이너리 COPY 사용 여부 # 데이터베이스 연결 설정 datasource: query: hikari: # Bulk Insert에 맞춘 설정 connection-init-sql: | SET work_mem = '512MB'; SET maintenance_work_mem = '1GB'; SET synchronous_commit = 'off'; SET checkpoint_completion_target = 0.9; # 락 설정 lock: timeout: 10 # Advisory Lock 타임아웃 (초) max-retry: 3 # 최대 재시도 횟수 # Writer 설정 writer: use-advisory-lock: false # Advisory Lock 사용 여부 (개별 처리 → 배치 처리) parallel-threads: 4 # 병렬 처리 스레드 수 # 재시도 설정 retry: max-attempts: 3 initial-interval: 100 max-interval: 5000 multiplier: 2 # 커스텀 Health Indicator 설정 health: job-timeout-hours: 6 # Job 타임아웃 시간 min-partition-count: 5 # 최소 파티션 수 grid: mode: haegu haegu: cache-enabled: true cache-size: 2000 # 선박 최신 위치 캐시 설정 cache: latest-position: enabled: false # 기본값: 비활성화 (프로파일별로 활성화) ttl-minutes: 60 # 캐시 TTL: 60분 max-size: 50000 # 최대 선박 수: 50,000척 refresh-interval-minutes: 2 # 갱신 주기 데이터 범위: 2분치 조회 (수집 지연 고려) # 이전 버킷 위치 캐시 설정 (버킷 간 점프 검출용) previous-bucket: ttl-minutes: 120 # 캐시 TTL: 120분 (위성 AIS 30~60분 간격 고려) max-size: 100000 # 최대 선박 수: 100,000척 (2시간 누적 고려) # ==================== S&P Global AIS API 설정 ==================== app: ais-api: url: ${AIS_API_URL:https://aisapi.maritime.spglobal.com} username: ${AIS_API_USERNAME:} password: ${AIS_API_PASSWORD:} since-seconds: 60 # API 조회 범위 (초) chunk-size: 50000 # 배치 청크 크기 (API 1회 호출 ~33K건) cache: ais-target: ttl-minutes: 120 # 기본 TTL (프로파일별 오버라이드) max-size: 300000 # 최대 캐시 크기 (30만 건) five-min-track: ttl-minutes: 75 # TTL 75분 (1시간 + 15분 여유) max-size: 1500000 # 700K→1.5M (실측 612K, 2.5배 여유) hourly-track: ttl-hours: 26 # TTL 26시간 (24시간 + 2시간 여유) max-size: 5000000 # 3.5M→5M (실측 3.5M 100% 도달, 추이 관찰 후 조정) chnprmship: mmsi-resource-path: classpath:chnprmship-mmsi.txt ttl-days: 7 max-size: 2000 warmup-enabled: true warmup-days: 7 # Swagger/OpenAPI 설정 springdoc: api-docs: enabled: true path: /v3/api-docs swagger-ui: enabled: true path: /swagger-ui.html display-request-duration: true disable-swagger-default-url: true doc-expansion: none operations-sorter: alpha tags-sorter: alpha show-actuator: true default-produces-media-type: application/json default-consumes-media-type: application/json