feat(maintenance): 배치 로그 관리 정리 Job 구현 (#16) #20
@ -4,6 +4,9 @@
|
|||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
### 추가
|
||||||
|
- 배치 로그 관리 정리 Job 구현: 90일 보관 기간 기준 자동 삭제 (#16)
|
||||||
|
|
||||||
## [2026-03-25]
|
## [2026-03-25]
|
||||||
|
|
||||||
### 추가
|
### 추가
|
||||||
|
|||||||
@ -0,0 +1,63 @@
|
|||||||
|
package com.snp.batch.jobs.maintenance;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.batch.core.Job;
|
||||||
|
import org.springframework.batch.core.Step;
|
||||||
|
import org.springframework.batch.core.job.builder.JobBuilder;
|
||||||
|
import org.springframework.batch.core.repository.JobRepository;
|
||||||
|
import org.springframework.batch.core.step.builder.StepBuilder;
|
||||||
|
import org.springframework.batch.core.step.tasklet.Tasklet;
|
||||||
|
import org.springframework.batch.repeat.RepeatStatus;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.transaction.PlatformTransactionManager;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 배치 로그 정리 Job
|
||||||
|
* 보관 기간이 지난 Spring Batch 메타데이터를 삭제
|
||||||
|
*
|
||||||
|
* 실행 주기: 매주 일요일 00:00 (Quartz 스케줄)
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Configuration
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class BatchLogCleanupJobConfig {
|
||||||
|
|
||||||
|
private final JobRepository jobRepository;
|
||||||
|
private final PlatformTransactionManager transactionManager;
|
||||||
|
private final BatchLogCleanupRepository cleanupRepository;
|
||||||
|
|
||||||
|
@Value("${app.batch.log-retention-days:90}")
|
||||||
|
private int retentionDays;
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public Job batchLogCleanupJob() {
|
||||||
|
return new JobBuilder("batchLogCleanupJob", jobRepository)
|
||||||
|
.start(batchLogCleanupStep())
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public Step batchLogCleanupStep() {
|
||||||
|
return new StepBuilder("batchLogCleanupStep", jobRepository)
|
||||||
|
.tasklet(batchLogCleanupTasklet(), transactionManager)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public Tasklet batchLogCleanupTasklet() {
|
||||||
|
return (contribution, chunkContext) -> {
|
||||||
|
LocalDateTime before = LocalDateTime.now().minusDays(retentionDays);
|
||||||
|
log.info("[LogCleanup] 배치 로그 정리 시작 (보관 기간: {}일, 기준일시: {})", retentionDays, before);
|
||||||
|
|
||||||
|
int totalDeleted = cleanupRepository.deleteOldLogs(before);
|
||||||
|
|
||||||
|
log.info("[LogCleanup] 배치 로그 정리 완료 (총 {}건 삭제)", totalDeleted);
|
||||||
|
return RepeatStatus.FINISHED;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,93 @@
|
|||||||
|
package com.snp.batch.jobs.maintenance;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.jdbc.core.JdbcTemplate;
|
||||||
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 배치 로그 삭제 Repository
|
||||||
|
* FK 제약 조건 순서를 고려하여 삭제
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Repository
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class BatchLogCleanupRepository {
|
||||||
|
|
||||||
|
private final JdbcTemplate jdbcTemplate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 보관 기간이 지난 배치 로그를 삭제
|
||||||
|
* 삭제 순서: step_context → step_execution → job_context → job_params → job_execution → job_instance(고아)
|
||||||
|
*
|
||||||
|
* @param before 이 시점 이전의 로그를 삭제
|
||||||
|
* @return 삭제된 총 행 수
|
||||||
|
*/
|
||||||
|
public int deleteOldLogs(LocalDateTime before) {
|
||||||
|
int total = 0;
|
||||||
|
|
||||||
|
// 1. batch_step_execution_context (step_execution FK)
|
||||||
|
int stepCtx = jdbcTemplate.update("""
|
||||||
|
DELETE FROM batch_step_execution_context
|
||||||
|
WHERE STEP_EXECUTION_ID IN (
|
||||||
|
SELECT se.STEP_EXECUTION_ID
|
||||||
|
FROM batch_step_execution se
|
||||||
|
INNER JOIN batch_job_execution je ON se.JOB_EXECUTION_ID = je.JOB_EXECUTION_ID
|
||||||
|
WHERE je.START_TIME < ?
|
||||||
|
)
|
||||||
|
""", before);
|
||||||
|
log.info("[LogCleanup] batch_step_execution_context: {}건 삭제", stepCtx);
|
||||||
|
total += stepCtx;
|
||||||
|
|
||||||
|
// 2. batch_step_execution
|
||||||
|
int stepExec = jdbcTemplate.update("""
|
||||||
|
DELETE FROM batch_step_execution
|
||||||
|
WHERE JOB_EXECUTION_ID IN (
|
||||||
|
SELECT JOB_EXECUTION_ID FROM batch_job_execution WHERE START_TIME < ?
|
||||||
|
)
|
||||||
|
""", before);
|
||||||
|
log.info("[LogCleanup] batch_step_execution: {}건 삭제", stepExec);
|
||||||
|
total += stepExec;
|
||||||
|
|
||||||
|
// 3. batch_job_execution_context
|
||||||
|
int jobCtx = jdbcTemplate.update("""
|
||||||
|
DELETE FROM batch_job_execution_context
|
||||||
|
WHERE JOB_EXECUTION_ID IN (
|
||||||
|
SELECT JOB_EXECUTION_ID FROM batch_job_execution WHERE START_TIME < ?
|
||||||
|
)
|
||||||
|
""", before);
|
||||||
|
log.info("[LogCleanup] batch_job_execution_context: {}건 삭제", jobCtx);
|
||||||
|
total += jobCtx;
|
||||||
|
|
||||||
|
// 4. batch_job_execution_params
|
||||||
|
int jobParams = jdbcTemplate.update("""
|
||||||
|
DELETE FROM batch_job_execution_params
|
||||||
|
WHERE JOB_EXECUTION_ID IN (
|
||||||
|
SELECT JOB_EXECUTION_ID FROM batch_job_execution WHERE START_TIME < ?
|
||||||
|
)
|
||||||
|
""", before);
|
||||||
|
log.info("[LogCleanup] batch_job_execution_params: {}건 삭제", jobParams);
|
||||||
|
total += jobParams;
|
||||||
|
|
||||||
|
// 5. batch_job_execution
|
||||||
|
int jobExec = jdbcTemplate.update("""
|
||||||
|
DELETE FROM batch_job_execution WHERE START_TIME < ?
|
||||||
|
""", before);
|
||||||
|
log.info("[LogCleanup] batch_job_execution: {}건 삭제", jobExec);
|
||||||
|
total += jobExec;
|
||||||
|
|
||||||
|
// 6. batch_job_instance (고아 인스턴스)
|
||||||
|
int jobInst = jdbcTemplate.update("""
|
||||||
|
DELETE FROM batch_job_instance
|
||||||
|
WHERE JOB_INSTANCE_ID NOT IN (
|
||||||
|
SELECT JOB_INSTANCE_ID FROM batch_job_execution
|
||||||
|
)
|
||||||
|
""");
|
||||||
|
log.info("[LogCleanup] batch_job_instance (고아): {}건 삭제", jobInst);
|
||||||
|
total += jobInst;
|
||||||
|
|
||||||
|
return total;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -104,6 +104,7 @@ app:
|
|||||||
batch:
|
batch:
|
||||||
chunk-size: 10000
|
chunk-size: 10000
|
||||||
sub-chunk-size: 5000 # Writer Sub-Chunk 분할 크기
|
sub-chunk-size: 5000 # Writer Sub-Chunk 분할 크기
|
||||||
|
log-retention-days: 90 # 배치 로그 보관 기간 (일)
|
||||||
source-schema:
|
source-schema:
|
||||||
name: std_snp_data
|
name: std_snp_data
|
||||||
tables:
|
tables:
|
||||||
|
|||||||
불러오는 중...
Reference in New Issue
Block a user