Compare commits

...

2 커밋

12개의 변경된 파일48개의 추가작업 그리고 48개의 파일을 삭제

파일 보기

@ -4,7 +4,7 @@
## 기술 스택
- Java 17, Spring Boot 3.2.1, Spring Batch 5.1.0
- PostgreSQL (스키마: t_std_snp_data)
- PostgreSQL (스키마: std_snp_data)
- Quartz Scheduler (JDBC Store)
- Spring Kafka (AIS Target → Kafka 파이프라인)
- WebFlux WebClient (외부 API 호출)

파일 보기

@ -1,6 +1,6 @@
-- ============================================================
-- ChnPrmShip 캐시 검증 진단 쿼리
-- 대상: t_std_snp_data.ais_target (일별 파티션)
-- 대상: std_snp_data.ais_target (일별 파티션)
-- 목적: 최근 2일 내 대상 MMSI별 최종위치 캐싱 검증
-- ============================================================
@ -22,7 +22,7 @@ SELECT
(SELECT COUNT(*) FROM tmp_chn_mmsi) - COUNT(DISTINCT a.mmsi) AS mmsi_without_data_2d,
ROUND(COUNT(DISTINCT a.mmsi) * 100.0
/ NULLIF((SELECT COUNT(*) FROM tmp_chn_mmsi), 0), 1) AS hit_rate_pct
FROM t_std_snp_data.ais_target a
FROM std_snp_data.ais_target a
JOIN tmp_chn_mmsi t ON a.mmsi = t.mmsi
WHERE a.message_timestamp >= NOW() - INTERVAL '2 days';
@ -37,7 +37,7 @@ SELECT COUNT(*) AS cached_count,
NOW() - MAX(message_timestamp) AS newest_age
FROM (
SELECT DISTINCT ON (a.mmsi) a.mmsi, a.message_timestamp
FROM t_std_snp_data.ais_target a
FROM std_snp_data.ais_target a
JOIN tmp_chn_mmsi t ON a.mmsi = t.mmsi
WHERE a.message_timestamp >= NOW() - INTERVAL '2 days'
ORDER BY a.mmsi, a.message_timestamp DESC
@ -58,7 +58,7 @@ SELECT DISTINCT ON (a.mmsi)
a.cog,
a.heading,
NOW() - a.message_timestamp AS data_age
FROM t_std_snp_data.ais_target a
FROM std_snp_data.ais_target a
JOIN tmp_chn_mmsi t ON a.mmsi = t.mmsi
WHERE a.message_timestamp >= NOW() - INTERVAL '2 days'
ORDER BY a.mmsi, a.message_timestamp DESC
@ -72,7 +72,7 @@ SELECT t.mmsi AS missing_mmsi
FROM tmp_chn_mmsi t
LEFT JOIN (
SELECT DISTINCT mmsi
FROM t_std_snp_data.ais_target
FROM std_snp_data.ais_target
WHERE mmsi IN (SELECT mmsi FROM tmp_chn_mmsi)
AND message_timestamp >= NOW() - INTERVAL '2 days'
) a ON t.mmsi = a.mmsi
@ -86,31 +86,31 @@ ORDER BY t.mmsi;
SELECT
'6시간 이내' AS time_range,
COUNT(DISTINCT mmsi) AS distinct_mmsi
FROM t_std_snp_data.ais_target a
FROM std_snp_data.ais_target a
JOIN tmp_chn_mmsi t ON a.mmsi = t.mmsi
WHERE a.message_timestamp >= NOW() - INTERVAL '6 hours'
UNION ALL
SELECT '12시간 이내', COUNT(DISTINCT mmsi)
FROM t_std_snp_data.ais_target a
FROM std_snp_data.ais_target a
JOIN tmp_chn_mmsi t ON a.mmsi = t.mmsi
WHERE a.message_timestamp >= NOW() - INTERVAL '12 hours'
UNION ALL
SELECT '1일 이내', COUNT(DISTINCT mmsi)
FROM t_std_snp_data.ais_target a
FROM std_snp_data.ais_target a
JOIN tmp_chn_mmsi t ON a.mmsi = t.mmsi
WHERE a.message_timestamp >= NOW() - INTERVAL '1 day'
UNION ALL
SELECT '2일 이내', COUNT(DISTINCT mmsi)
FROM t_std_snp_data.ais_target a
FROM std_snp_data.ais_target a
JOIN tmp_chn_mmsi t ON a.mmsi = t.mmsi
WHERE a.message_timestamp >= NOW() - INTERVAL '2 days'
UNION ALL
SELECT '전체(무제한)', COUNT(DISTINCT mmsi)
FROM t_std_snp_data.ais_target a
FROM std_snp_data.ais_target a
JOIN tmp_chn_mmsi t ON a.mmsi = t.mmsi;
@ -123,7 +123,7 @@ SELECT
COUNT(DISTINCT mmsi) AS distinct_mmsi,
MIN(message_timestamp) AS min_ts,
MAX(message_timestamp) AS max_ts
FROM t_std_snp_data.ais_target a
FROM std_snp_data.ais_target a
JOIN tmp_chn_mmsi t ON a.mmsi = t.mmsi
GROUP BY tableoid::regclass
ORDER BY max_ts DESC;
@ -139,7 +139,7 @@ SELECT
FROM pg_inherits i
JOIN pg_class c ON c.oid = i.inhrelid
JOIN pg_stat_user_tables s ON s.relid = c.oid
WHERE i.inhparent = 't_std_snp_data.ais_target'::regclass
WHERE i.inhparent = 'std_snp_data.ais_target'::regclass
ORDER BY c.relname DESC;

파일 보기

@ -7,7 +7,7 @@ import org.hibernate.annotations.CreationTimestamp;
import java.time.LocalDateTime;
@Entity
@Table(name = "batch_api_log", schema = "t_std_snp_data")
@Table(name = "batch_api_log", schema = "std_snp_data")
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor

파일 보기

@ -7,7 +7,7 @@ import org.hibernate.annotations.CreationTimestamp;
import java.time.LocalDateTime;
@Entity
@Table(name = "batch_failed_record", schema = "t_std_snp_data")
@Table(name = "batch_failed_record", schema = "std_snp_data")
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor

파일 보기

@ -15,12 +15,12 @@ import java.util.Optional;
* 설정 예시:
* app.batch.partition:
* daily-tables:
* - schema: snp_data
* - schema: std_snp_data
* table-name: ais_target
* partition-column: message_timestamp
* periods-ahead: 3
* monthly-tables:
* - schema: snp_data
* - schema: std_snp_data
* table-name: some_table
* partition-column: created_at
* periods-ahead: 2
@ -58,7 +58,7 @@ public class PartitionConfig {
@Getter
@Setter
public static class PartitionTableConfig {
private String schema = "snp_data";
private String schema = "std_snp_data";
private String tableName;
private String partitionColumn;
private int periodsAhead = 3; // 미리 생성할 기간 (daily: , monthly: )

파일 보기

@ -13,7 +13,7 @@ import java.time.OffsetDateTime;
/**
* AIS Target Entity
*
* 테이블: snp_data.ais_target
* 테이블: std_snp_data.ais_target
* PK: mmsi + message_timestamp (복합키)
*
* 용도:

파일 보기

@ -13,8 +13,8 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
* 프로파일별 설정 파일에서 지정할 있도록 구성
*
* 사용 :
* - dev: snp_data.core20 (ihslrorimoshipno, maritimemobileserviceidentitymmsinumber)
* - prod: new_snp.core20 (lrno, mmsi)
* - dev: std_snp_svc.tb_ship_main_info (imo_no, mmsi_no)
* - prod: std_snp_svc.tb_ship_main_info (imo_no, mmsi_no)
*/
@Slf4j
@Getter
@ -23,24 +23,24 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
public class Core20Properties {
/**
* 스키마명 (: snp_data, new_snp)
* 스키마명 (: std_snp_svc)
*/
private String schema = "snp_data";
private String schema = "std_snp_svc";
/**
* 테이블명 (: core20)
* 테이블명 (: tb_ship_main_info)
*/
private String table = "core20";
private String table = "tb_ship_main_info";
/**
* IMO/LRNO 컬럼명 (PK, NOT NULL)
*/
private String imoColumn = "ihslrorimoshipno";
private String imoColumn = "imo_no";
/**
* MMSI 컬럼명 (NULLABLE)
*/
private String mmsiColumn = "maritimemobileserviceidentitymmsinumber";
private String mmsiColumn = "mmsi_no";
/**
* 전체 테이블명 반환 (schema.table)

파일 보기

@ -22,12 +22,12 @@ spring:
hibernate:
dialect: org.hibernate.dialect.PostgreSQLDialect
format_sql: true
default_schema: t_std_snp_data
default_schema: std_snp_data
# Batch Configuration
batch:
jdbc:
table-prefix: "t_std_snp_data.batch_"
table-prefix: "std_snp_data.batch_"
initialize-schema: always # Changed to 'never' as tables already exist
job:
enabled: false # Prevent auto-run on startup
@ -49,7 +49,7 @@ spring:
org.quartz.threadPool.threadCount: 10
org.quartz.jobStore.class: org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.driverDelegateClass: org.quartz.impl.jdbcjobstore.PostgreSQLDelegate
org.quartz.jobStore.tablePrefix: t_std_snp_data.QRTZ_
org.quartz.jobStore.tablePrefix: std_snp_data.QRTZ_
org.quartz.jobStore.isClustered: false
org.quartz.jobStore.misfireThreshold: 60000
@ -142,7 +142,7 @@ app:
# Core20 캐시 테이블 설정 (환경별로 테이블/컬럼명이 다를 수 있음)
core20:
schema: t_std_snp_svc # 스키마명
schema: std_snp_svc # 스키마명
table: tb_ship_main_info # 테이블명
imo-column: imo_no # IMO/LRNO 컬럼명 (PK, NOT NULL)
mmsi-column: mmsi_no # MMSI 컬럼명 (NULLABLE)
@ -151,7 +151,7 @@ app:
partition:
# 일별 파티션 테이블 목록 (네이밍: {table}_YYMMDD)
daily-tables:
- schema: t_std_snp_data
- schema: std_snp_data
table-name: ais_target
partition-column: message_timestamp
periods-ahead: 3 # 미리 생성할 일수

파일 보기

@ -22,12 +22,12 @@ spring:
hibernate:
dialect: org.hibernate.dialect.PostgreSQLDialect
format_sql: true
default_schema: t_std_snp_data
default_schema: std_snp_data
# Batch Configuration
batch:
jdbc:
table-prefix: "t_std_snp_data.batch_"
table-prefix: "std_snp_data.batch_"
initialize-schema: never # Changed to 'never' as tables already exist
job:
enabled: false # Prevent auto-run on startup
@ -49,7 +49,7 @@ spring:
org.quartz.threadPool.threadCount: 10
org.quartz.jobStore.class: org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.driverDelegateClass: org.quartz.impl.jdbcjobstore.PostgreSQLDelegate
org.quartz.jobStore.tablePrefix: t_std_snp_data.QRTZ_
org.quartz.jobStore.tablePrefix: std_snp_data.QRTZ_
org.quartz.jobStore.isClustered: false
org.quartz.jobStore.misfireThreshold: 60000
@ -144,7 +144,7 @@ app:
# Core20 캐시 테이블 설정 (환경별로 테이블/컬럼명이 다를 수 있음)
core20:
schema: t_std_snp_svc # 스키마명
schema: std_snp_svc # 스키마명
table: tb_ship_main_info # 테이블명
imo-column: imo_no # IMO/LRNO 컬럼명 (PK, NOT NULL)
mmsi-column: mmsi_no # MMSI 컬럼명 (NULLABLE)
@ -153,7 +153,7 @@ app:
partition:
# 일별 파티션 테이블 목록 (네이밍: {table}_YYMMDD)
daily-tables:
- schema: t_std_snp_data
- schema: std_snp_data
table-name: ais_target
partition-column: message_timestamp
periods-ahead: 3 # 미리 생성할 일수

파일 보기

@ -22,12 +22,12 @@ spring:
hibernate:
dialect: org.hibernate.dialect.PostgreSQLDialect
format_sql: true
default_schema: t_std_snp_data
default_schema: std_snp_data
# Batch Configuration
batch:
jdbc:
table-prefix: "t_std_snp_data.batch_"
table-prefix: "std_snp_data.batch_"
initialize-schema: never # Changed to 'never' as tables already exist
job:
enabled: false # Prevent auto-run on startup
@ -49,7 +49,7 @@ spring:
org.quartz.threadPool.threadCount: 10
org.quartz.jobStore.class: org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.driverDelegateClass: org.quartz.impl.jdbcjobstore.PostgreSQLDelegate
org.quartz.jobStore.tablePrefix: t_std_snp_data.QRTZ_
org.quartz.jobStore.tablePrefix: std_snp_data.QRTZ_
org.quartz.jobStore.isClustered: false
org.quartz.jobStore.misfireThreshold: 60000
@ -95,7 +95,7 @@ app:
batch:
chunk-size: 1000
target-schema:
name: t_std_snp_data
name: std_snp_data
tables:
ship-001: tb_ship_default_info
ship-002: tb_ship_info_mst
@ -147,7 +147,7 @@ app:
risk-compliance-003: tb_company_compliance_info
ship-028: ship_detail_hash_json
service-schema:
name: t_std_snp_svc
name: std_snp_svc
tables:
service-001: tb_ship_main_info
api:
@ -212,7 +212,7 @@ app:
# Core20 캐시 테이블 설정 (환경별로 테이블/컬럼명이 다를 수 있음)
core20:
schema: t_std_snp_svc # 스키마명
schema: std_snp_svc # 스키마명
table: tb_ship_main_info # 테이블명
imo-column: imo_no # IMO/LRNO 컬럼명 (PK, NOT NULL)
mmsi-column: mmsi_no # MMSI 컬럼명 (NULLABLE)
@ -221,7 +221,7 @@ app:
partition:
# 일별 파티션 테이블 목록 (네이밍: {table}_YYMMDD)
daily-tables:
- schema: t_std_snp_data
- schema: std_snp_data
table-name: ais_target
partition-column: message_timestamp
periods-ahead: 3 # 미리 생성할 일수

파일 보기

@ -154,7 +154,7 @@ BEGIN
RETURN EXISTS (
SELECT 1 FROM pg_class c
JOIN pg_namespace n ON n.oid = c.relnamespace
WHERE n.nspname = 'snp_data'
WHERE n.nspname = 'std_snp_data'
AND c.relname = partition_name
AND c.relkind = 'r'
);
@ -261,7 +261,7 @@ BEGIN
FROM pg_class c
JOIN pg_namespace n ON n.oid = c.relnamespace
JOIN pg_inherits i ON i.inhrelid = c.oid
WHERE n.nspname = 'snp_data'
WHERE n.nspname = 'std_snp_data'
AND c.relname LIKE 'ais_target_%'
AND LENGTH(c.relname) = 17 -- ais_target_YYMMDD = 17자
AND c.relkind = 'r'
@ -304,7 +304,7 @@ BEGIN
FROM pg_class c
JOIN pg_namespace n ON n.oid = c.relnamespace
JOIN pg_inherits i ON i.inhrelid = c.oid
WHERE n.nspname = 'snp_data'
WHERE n.nspname = 'std_snp_data'
AND c.relname LIKE 'ais_target_%'
AND c.relkind = 'r'
ORDER BY c.relname;

파일 보기

@ -1,5 +1,5 @@
-- 오래된 STARTED 상태 Job을 정리하는 SQL 쿼리입니다.
-- snp_data 스키마에 batch_ 접두사를 사용하는 예시입니다. 실제 스키마에 맞추어 수정해서 사용하세요.
-- std_snp_data 스키마에 batch_ 접두사를 사용하는 예시입니다. 실제 스키마에 맞추어 수정해서 사용하세요.
-- 참고: 시간 간격 변경이 필요하면 INTERVAL '2 hours' 부분을 수정하세요:
-- 1시간: INTERVAL '1 hour'