feature/ISSUE-002-empty-response-catch #13
@ -13,6 +13,8 @@ import org.springframework.batch.core.Job;
|
|||||||
import org.springframework.batch.core.Step;
|
import org.springframework.batch.core.Step;
|
||||||
import org.springframework.batch.core.configuration.annotation.StepScope;
|
import org.springframework.batch.core.configuration.annotation.StepScope;
|
||||||
import org.springframework.batch.core.job.builder.JobBuilder;
|
import org.springframework.batch.core.job.builder.JobBuilder;
|
||||||
|
import org.springframework.batch.core.job.flow.FlowExecutionStatus;
|
||||||
|
import org.springframework.batch.core.job.flow.JobExecutionDecider;
|
||||||
import org.springframework.batch.core.repository.JobRepository;
|
import org.springframework.batch.core.repository.JobRepository;
|
||||||
import org.springframework.batch.core.step.builder.StepBuilder;
|
import org.springframework.batch.core.step.builder.StepBuilder;
|
||||||
import org.springframework.batch.core.step.tasklet.Tasklet;
|
import org.springframework.batch.core.step.tasklet.Tasklet;
|
||||||
@ -50,9 +52,15 @@ public class CompanyComplianceImportRangeJobConfig extends BaseMultiStepJobConfi
|
|||||||
|
|
||||||
@Value("${app.batch.target-schema.name}")
|
@Value("${app.batch.target-schema.name}")
|
||||||
private String targetSchema;
|
private String targetSchema;
|
||||||
|
@Value("${app.batch.last-execution-buffer-hours:24}")
|
||||||
|
private int lastExecutionBufferHours;
|
||||||
|
|
||||||
protected String getApiKey() {return "COMPANY_COMPLIANCE_IMPORT_API";}
|
protected String getApiKey() {return "COMPANY_COMPLIANCE_IMPORT_API";}
|
||||||
protected String getBatchUpdateSql() {
|
protected String getBatchUpdateSql() {
|
||||||
return String.format("UPDATE %s.BATCH_LAST_EXECUTION SET LAST_SUCCESS_DATE = NOW(), UPDATED_AT = NOW() WHERE API_KEY = '%s'", targetSchema, getApiKey());}
|
return String.format(
|
||||||
|
"UPDATE %s.BATCH_LAST_EXECUTION SET LAST_SUCCESS_DATE = NOW() - INTERVAL '%d HOURS', UPDATED_AT = NOW() WHERE API_KEY = '%s'",
|
||||||
|
targetSchema, lastExecutionBufferHours, getApiKey());
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected int getChunkSize() {
|
protected int getChunkSize() {
|
||||||
@ -90,12 +98,26 @@ public class CompanyComplianceImportRangeJobConfig extends BaseMultiStepJobConfi
|
|||||||
@Override
|
@Override
|
||||||
protected Job createJobFlow(JobBuilder jobBuilder) {
|
protected Job createJobFlow(JobBuilder jobBuilder) {
|
||||||
return jobBuilder
|
return jobBuilder
|
||||||
.start(companyComplianceImportRangeStep()) // 1단계 실행
|
.start(companyComplianceImportRangeStep())
|
||||||
// .next(companyComplianceHistoryValueChangeManageStep()) // 2단계 실행 (2단계 실패 시 실행 안 됨)
|
.next(companyComplianceEmptyResponseDecider())
|
||||||
.next(companyComplianceLastExecutionUpdateStep()) // 3단계: 모두 완료 시, BATCH_LAST_EXECUTION 마지막 성공일자 업데이트
|
.on("EMPTY_RESPONSE").end()
|
||||||
|
.from(companyComplianceEmptyResponseDecider()).on("*").to(companyComplianceLastExecutionUpdateStep())
|
||||||
|
.end()
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public JobExecutionDecider companyComplianceEmptyResponseDecider() {
|
||||||
|
return (jobExecution, stepExecution) -> {
|
||||||
|
if (stepExecution != null && stepExecution.getReadCount() == 0) {
|
||||||
|
log.info("[CompanyComplianceImportRangeJob] Decider: EMPTY_RESPONSE - 응답 데이터 0건으로 LAST_EXECUTION 업데이트 스킵");
|
||||||
|
return new FlowExecutionStatus("EMPTY_RESPONSE");
|
||||||
|
}
|
||||||
|
log.info("[CompanyComplianceImportRangeJob] Decider: NORMAL - LAST_EXECUTION 업데이트 진행");
|
||||||
|
return new FlowExecutionStatus("NORMAL");
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected ItemReader<CompanyComplianceDto> createReader() {
|
protected ItemReader<CompanyComplianceDto> createReader() {
|
||||||
return companyComplianceDataRangeReader;
|
return companyComplianceDataRangeReader;
|
||||||
@ -193,11 +215,11 @@ public class CompanyComplianceImportRangeJobConfig extends BaseMultiStepJobConfi
|
|||||||
@Bean
|
@Bean
|
||||||
public Tasklet companyComplianceLastExecutionUpdateTasklet() {
|
public Tasklet companyComplianceLastExecutionUpdateTasklet() {
|
||||||
return (contribution, chunkContext) -> {
|
return (contribution, chunkContext) -> {
|
||||||
log.info(">>>>> 모든 스텝 성공: BATCH_LAST_EXECUTION 업데이트 시작");
|
log.info(">>>>> 모든 스텝 성공: BATCH_LAST_EXECUTION 업데이트 시작 (버퍼: {}시간)", lastExecutionBufferHours);
|
||||||
|
|
||||||
jdbcTemplate.execute(getBatchUpdateSql());
|
jdbcTemplate.execute(getBatchUpdateSql());
|
||||||
|
|
||||||
log.info(">>>>> BATCH_LAST_EXECUTION 업데이트 완료");
|
log.info(">>>>> BATCH_LAST_EXECUTION 업데이트 완료 (LAST_SUCCESS_DATE = NOW() - {}시간)", lastExecutionBufferHours);
|
||||||
return RepeatStatus.FINISHED;
|
return RepeatStatus.FINISHED;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,6 +13,8 @@ import org.springframework.batch.core.Job;
|
|||||||
import org.springframework.batch.core.Step;
|
import org.springframework.batch.core.Step;
|
||||||
import org.springframework.batch.core.configuration.annotation.StepScope;
|
import org.springframework.batch.core.configuration.annotation.StepScope;
|
||||||
import org.springframework.batch.core.job.builder.JobBuilder;
|
import org.springframework.batch.core.job.builder.JobBuilder;
|
||||||
|
import org.springframework.batch.core.job.flow.FlowExecutionStatus;
|
||||||
|
import org.springframework.batch.core.job.flow.JobExecutionDecider;
|
||||||
import org.springframework.batch.core.repository.JobRepository;
|
import org.springframework.batch.core.repository.JobRepository;
|
||||||
import org.springframework.batch.core.step.builder.StepBuilder;
|
import org.springframework.batch.core.step.builder.StepBuilder;
|
||||||
import org.springframework.batch.core.step.tasklet.Tasklet;
|
import org.springframework.batch.core.step.tasklet.Tasklet;
|
||||||
@ -49,9 +51,15 @@ public class ComplianceImportRangeJobConfig extends BaseMultiStepJobConfig<Compl
|
|||||||
|
|
||||||
@Value("${app.batch.target-schema.name}")
|
@Value("${app.batch.target-schema.name}")
|
||||||
private String targetSchema;
|
private String targetSchema;
|
||||||
|
@Value("${app.batch.last-execution-buffer-hours:24}")
|
||||||
|
private int lastExecutionBufferHours;
|
||||||
|
|
||||||
protected String getApiKey() {return "COMPLIANCE_IMPORT_API";}
|
protected String getApiKey() {return "COMPLIANCE_IMPORT_API";}
|
||||||
protected String getBatchUpdateSql() {
|
protected String getBatchUpdateSql() {
|
||||||
return String.format("UPDATE %s.BATCH_LAST_EXECUTION SET LAST_SUCCESS_DATE = NOW(), UPDATED_AT = NOW() WHERE API_KEY = '%s'", targetSchema, getApiKey());}
|
return String.format(
|
||||||
|
"UPDATE %s.BATCH_LAST_EXECUTION SET LAST_SUCCESS_DATE = NOW() - INTERVAL '%d HOURS', UPDATED_AT = NOW() WHERE API_KEY = '%s'",
|
||||||
|
targetSchema, lastExecutionBufferHours, getApiKey());
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected int getChunkSize() {
|
protected int getChunkSize() {
|
||||||
@ -90,12 +98,26 @@ public class ComplianceImportRangeJobConfig extends BaseMultiStepJobConfig<Compl
|
|||||||
@Override
|
@Override
|
||||||
protected Job createJobFlow(JobBuilder jobBuilder) {
|
protected Job createJobFlow(JobBuilder jobBuilder) {
|
||||||
return jobBuilder
|
return jobBuilder
|
||||||
.start(complianceImportRangeStep()) // 1단계 실행
|
.start(complianceImportRangeStep())
|
||||||
// .next(complianceHistoryValueChangeManageStep()) // 2단계 실행 (2단계 실패 시 실행 안 됨)
|
.next(complianceEmptyResponseDecider())
|
||||||
.next(complianceLastExecutionUpdateStep()) // 3단계: 모두 완료 시, BATCH_LAST_EXECUTION 마지막 성공일자 업데이트
|
.on("EMPTY_RESPONSE").end()
|
||||||
|
.from(complianceEmptyResponseDecider()).on("*").to(complianceLastExecutionUpdateStep())
|
||||||
|
.end()
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public JobExecutionDecider complianceEmptyResponseDecider() {
|
||||||
|
return (jobExecution, stepExecution) -> {
|
||||||
|
if (stepExecution != null && stepExecution.getReadCount() == 0) {
|
||||||
|
log.info("[ComplianceImportRangeJob] Decider: EMPTY_RESPONSE - 응답 데이터 0건으로 LAST_EXECUTION 업데이트 스킵");
|
||||||
|
return new FlowExecutionStatus("EMPTY_RESPONSE");
|
||||||
|
}
|
||||||
|
log.info("[ComplianceImportRangeJob] Decider: NORMAL - LAST_EXECUTION 업데이트 진행");
|
||||||
|
return new FlowExecutionStatus("NORMAL");
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected ItemReader<ComplianceDto> createReader() {
|
protected ItemReader<ComplianceDto> createReader() {
|
||||||
return complianceDataRangeReader;
|
return complianceDataRangeReader;
|
||||||
@ -195,11 +217,11 @@ public class ComplianceImportRangeJobConfig extends BaseMultiStepJobConfig<Compl
|
|||||||
@Bean
|
@Bean
|
||||||
public Tasklet complianceLastExecutionUpdateTasklet() {
|
public Tasklet complianceLastExecutionUpdateTasklet() {
|
||||||
return (contribution, chunkContext) -> {
|
return (contribution, chunkContext) -> {
|
||||||
log.info(">>>>> 모든 스텝 성공: BATCH_LAST_EXECUTION 업데이트 시작");
|
log.info(">>>>> 모든 스텝 성공: BATCH_LAST_EXECUTION 업데이트 시작 (버퍼: {}시간)", lastExecutionBufferHours);
|
||||||
|
|
||||||
jdbcTemplate.execute(getBatchUpdateSql());
|
jdbcTemplate.execute(getBatchUpdateSql());
|
||||||
|
|
||||||
log.info(">>>>> BATCH_LAST_EXECUTION 업데이트 완료");
|
log.info(">>>>> BATCH_LAST_EXECUTION 업데이트 완료 (LAST_SUCCESS_DATE = NOW() - {}시간)", lastExecutionBufferHours);
|
||||||
return RepeatStatus.FINISHED;
|
return RepeatStatus.FINISHED;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,6 +13,8 @@ import org.springframework.batch.core.Job;
|
|||||||
import org.springframework.batch.core.Step;
|
import org.springframework.batch.core.Step;
|
||||||
import org.springframework.batch.core.configuration.annotation.StepScope;
|
import org.springframework.batch.core.configuration.annotation.StepScope;
|
||||||
import org.springframework.batch.core.job.builder.JobBuilder;
|
import org.springframework.batch.core.job.builder.JobBuilder;
|
||||||
|
import org.springframework.batch.core.job.flow.FlowExecutionStatus;
|
||||||
|
import org.springframework.batch.core.job.flow.JobExecutionDecider;
|
||||||
import org.springframework.batch.core.repository.JobRepository;
|
import org.springframework.batch.core.repository.JobRepository;
|
||||||
import org.springframework.batch.core.step.builder.StepBuilder;
|
import org.springframework.batch.core.step.builder.StepBuilder;
|
||||||
import org.springframework.batch.core.step.tasklet.Tasklet;
|
import org.springframework.batch.core.step.tasklet.Tasklet;
|
||||||
@ -45,9 +47,15 @@ public class EventImportJobConfig extends BaseMultiStepJobConfig<EventDetailDto,
|
|||||||
@Value("${app.batch.target-schema.name}")
|
@Value("${app.batch.target-schema.name}")
|
||||||
private String targetSchema;
|
private String targetSchema;
|
||||||
|
|
||||||
|
@Value("${app.batch.last-execution-buffer-hours:24}")
|
||||||
|
private int lastExecutionBufferHours;
|
||||||
|
|
||||||
protected String getApiKey() {return "EVENT_IMPORT_API";}
|
protected String getApiKey() {return "EVENT_IMPORT_API";}
|
||||||
protected String getBatchUpdateSql() {
|
protected String getBatchUpdateSql() {
|
||||||
return String.format("UPDATE %s.BATCH_LAST_EXECUTION SET LAST_SUCCESS_DATE = NOW(), UPDATED_AT = NOW() WHERE API_KEY = '%s'", targetSchema, getApiKey());}
|
return String.format(
|
||||||
|
"UPDATE %s.BATCH_LAST_EXECUTION SET LAST_SUCCESS_DATE = NOW() - INTERVAL '%d HOURS', UPDATED_AT = NOW() WHERE API_KEY = '%s'",
|
||||||
|
targetSchema, lastExecutionBufferHours, getApiKey());
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected int getChunkSize() {
|
protected int getChunkSize() {
|
||||||
@ -87,10 +95,25 @@ public class EventImportJobConfig extends BaseMultiStepJobConfig<EventDetailDto,
|
|||||||
protected Job createJobFlow(JobBuilder jobBuilder) {
|
protected Job createJobFlow(JobBuilder jobBuilder) {
|
||||||
return jobBuilder
|
return jobBuilder
|
||||||
.start(eventImportStep())
|
.start(eventImportStep())
|
||||||
.next(eventLastExecutionUpdateStep())
|
.next(eventEmptyResponseDecider())
|
||||||
|
.on("EMPTY_RESPONSE").end()
|
||||||
|
.from(eventEmptyResponseDecider()).on("*").to(eventLastExecutionUpdateStep())
|
||||||
|
.end()
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public JobExecutionDecider eventEmptyResponseDecider() {
|
||||||
|
return (jobExecution, stepExecution) -> {
|
||||||
|
if (stepExecution != null && stepExecution.getReadCount() == 0) {
|
||||||
|
log.info("[EventImportJob] Decider: EMPTY_RESPONSE - 응답 데이터 0건으로 LAST_EXECUTION 업데이트 스킵");
|
||||||
|
return new FlowExecutionStatus("EMPTY_RESPONSE");
|
||||||
|
}
|
||||||
|
log.info("[EventImportJob] Decider: NORMAL - LAST_EXECUTION 업데이트 진행");
|
||||||
|
return new FlowExecutionStatus("NORMAL");
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
@StepScope
|
@StepScope
|
||||||
public EventDataReader eventDataReader(
|
public EventDataReader eventDataReader(
|
||||||
@ -139,11 +162,11 @@ public class EventImportJobConfig extends BaseMultiStepJobConfig<EventDetailDto,
|
|||||||
@Bean
|
@Bean
|
||||||
public Tasklet eventLastExecutionUpdateTasklet() {
|
public Tasklet eventLastExecutionUpdateTasklet() {
|
||||||
return (contribution, chunkContext) -> {
|
return (contribution, chunkContext) -> {
|
||||||
log.info(">>>>> 모든 스텝 성공: BATCH_LAST_EXECUTION 업데이트 시작");
|
log.info(">>>>> 모든 스텝 성공: BATCH_LAST_EXECUTION 업데이트 시작 (버퍼: {}시간)", lastExecutionBufferHours);
|
||||||
|
|
||||||
jdbcTemplate.execute(getBatchUpdateSql());
|
jdbcTemplate.execute(getBatchUpdateSql());
|
||||||
|
|
||||||
log.info(">>>>> BATCH_LAST_EXECUTION 업데이트 완료");
|
log.info(">>>>> BATCH_LAST_EXECUTION 업데이트 완료 (LAST_SUCCESS_DATE = NOW() - {}시간)", lastExecutionBufferHours);
|
||||||
return RepeatStatus.FINISHED;
|
return RepeatStatus.FINISHED;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,6 +15,8 @@ import org.springframework.batch.core.Job;
|
|||||||
import org.springframework.batch.core.Step;
|
import org.springframework.batch.core.Step;
|
||||||
import org.springframework.batch.core.configuration.annotation.StepScope;
|
import org.springframework.batch.core.configuration.annotation.StepScope;
|
||||||
import org.springframework.batch.core.job.builder.JobBuilder;
|
import org.springframework.batch.core.job.builder.JobBuilder;
|
||||||
|
import org.springframework.batch.core.job.flow.FlowExecutionStatus;
|
||||||
|
import org.springframework.batch.core.job.flow.JobExecutionDecider;
|
||||||
import org.springframework.batch.core.repository.JobRepository;
|
import org.springframework.batch.core.repository.JobRepository;
|
||||||
import org.springframework.batch.core.step.builder.StepBuilder;
|
import org.springframework.batch.core.step.builder.StepBuilder;
|
||||||
import org.springframework.batch.core.step.tasklet.Tasklet;
|
import org.springframework.batch.core.step.tasklet.Tasklet;
|
||||||
@ -47,9 +49,15 @@ public class PscInspectionJobConfig extends BaseMultiStepJobConfig<PscInspection
|
|||||||
@Value("${app.batch.target-schema.name}")
|
@Value("${app.batch.target-schema.name}")
|
||||||
private String targetSchema;
|
private String targetSchema;
|
||||||
|
|
||||||
|
@Value("${app.batch.last-execution-buffer-hours:24}")
|
||||||
|
private int lastExecutionBufferHours;
|
||||||
|
|
||||||
protected String getApiKey() {return "PSC_IMPORT_API";}
|
protected String getApiKey() {return "PSC_IMPORT_API";}
|
||||||
protected String getBatchUpdateSql() {
|
protected String getBatchUpdateSql() {
|
||||||
return String.format("UPDATE %s.BATCH_LAST_EXECUTION SET LAST_SUCCESS_DATE = NOW(), UPDATED_AT = NOW() WHERE API_KEY = '%s'", targetSchema, getApiKey());}
|
return String.format(
|
||||||
|
"UPDATE %s.BATCH_LAST_EXECUTION SET LAST_SUCCESS_DATE = NOW() - INTERVAL '%d HOURS', UPDATED_AT = NOW() WHERE API_KEY = '%s'",
|
||||||
|
targetSchema, lastExecutionBufferHours, getApiKey());
|
||||||
|
}
|
||||||
|
|
||||||
public PscInspectionJobConfig(
|
public PscInspectionJobConfig(
|
||||||
JobRepository jobRepository,
|
JobRepository jobRepository,
|
||||||
@ -87,10 +95,25 @@ public class PscInspectionJobConfig extends BaseMultiStepJobConfig<PscInspection
|
|||||||
protected Job createJobFlow(JobBuilder jobBuilder) {
|
protected Job createJobFlow(JobBuilder jobBuilder) {
|
||||||
return jobBuilder
|
return jobBuilder
|
||||||
.start(PSCDetailImportStep())
|
.start(PSCDetailImportStep())
|
||||||
.next(pscLastExecutionUpdateStep())
|
.next(pscEmptyResponseDecider())
|
||||||
|
.on("EMPTY_RESPONSE").end()
|
||||||
|
.from(pscEmptyResponseDecider()).on("*").to(pscLastExecutionUpdateStep())
|
||||||
|
.end()
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public JobExecutionDecider pscEmptyResponseDecider() {
|
||||||
|
return (jobExecution, stepExecution) -> {
|
||||||
|
if (stepExecution != null && stepExecution.getReadCount() == 0) {
|
||||||
|
log.info("[PSCDetailImportJob] Decider: EMPTY_RESPONSE - 응답 데이터 0건으로 LAST_EXECUTION 업데이트 스킵");
|
||||||
|
return new FlowExecutionStatus("EMPTY_RESPONSE");
|
||||||
|
}
|
||||||
|
log.info("[PSCDetailImportJob] Decider: NORMAL - LAST_EXECUTION 업데이트 진행");
|
||||||
|
return new FlowExecutionStatus("NORMAL");
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
@StepScope
|
@StepScope
|
||||||
public PscApiReader pscApiReader(
|
public PscApiReader pscApiReader(
|
||||||
@ -145,11 +168,11 @@ public class PscInspectionJobConfig extends BaseMultiStepJobConfig<PscInspection
|
|||||||
@Bean
|
@Bean
|
||||||
public Tasklet pscLastExecutionUpdateTasklet() {
|
public Tasklet pscLastExecutionUpdateTasklet() {
|
||||||
return (contribution, chunkContext) -> {
|
return (contribution, chunkContext) -> {
|
||||||
log.info(">>>>> 모든 스텝 성공: BATCH_LAST_EXECUTION 업데이트 시작");
|
log.info(">>>>> 모든 스텝 성공: BATCH_LAST_EXECUTION 업데이트 시작 (버퍼: {}시간)", lastExecutionBufferHours);
|
||||||
|
|
||||||
jdbcTemplate.execute(getBatchUpdateSql());
|
jdbcTemplate.execute(getBatchUpdateSql());
|
||||||
|
|
||||||
log.info(">>>>> BATCH_LAST_EXECUTION 업데이트 완료");
|
log.info(">>>>> BATCH_LAST_EXECUTION 업데이트 완료 (LAST_SUCCESS_DATE = NOW() - {}시간)", lastExecutionBufferHours);
|
||||||
return RepeatStatus.FINISHED;
|
return RepeatStatus.FINISHED;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,6 +13,8 @@ import org.springframework.batch.core.Job;
|
|||||||
import org.springframework.batch.core.Step;
|
import org.springframework.batch.core.Step;
|
||||||
import org.springframework.batch.core.configuration.annotation.StepScope;
|
import org.springframework.batch.core.configuration.annotation.StepScope;
|
||||||
import org.springframework.batch.core.job.builder.JobBuilder;
|
import org.springframework.batch.core.job.builder.JobBuilder;
|
||||||
|
import org.springframework.batch.core.job.flow.FlowExecutionStatus;
|
||||||
|
import org.springframework.batch.core.job.flow.JobExecutionDecider;
|
||||||
import org.springframework.batch.core.repository.JobRepository;
|
import org.springframework.batch.core.repository.JobRepository;
|
||||||
import org.springframework.batch.core.step.builder.StepBuilder;
|
import org.springframework.batch.core.step.builder.StepBuilder;
|
||||||
import org.springframework.batch.core.step.tasklet.Tasklet;
|
import org.springframework.batch.core.step.tasklet.Tasklet;
|
||||||
@ -45,9 +47,15 @@ public class RiskImportRangeJobConfig extends BaseMultiStepJobConfig<RiskDto, Ri
|
|||||||
@Value("${app.batch.target-schema.name}")
|
@Value("${app.batch.target-schema.name}")
|
||||||
private String targetSchema;
|
private String targetSchema;
|
||||||
|
|
||||||
|
@Value("${app.batch.last-execution-buffer-hours:24}")
|
||||||
|
private int lastExecutionBufferHours;
|
||||||
|
|
||||||
protected String getApiKey() {return "RISK_IMPORT_API";}
|
protected String getApiKey() {return "RISK_IMPORT_API";}
|
||||||
protected String getBatchUpdateSql() {
|
protected String getBatchUpdateSql() {
|
||||||
return String.format("UPDATE %s.BATCH_LAST_EXECUTION SET LAST_SUCCESS_DATE = NOW(), UPDATED_AT = NOW() WHERE API_KEY = '%s'", targetSchema, getApiKey());}
|
return String.format(
|
||||||
|
"UPDATE %s.BATCH_LAST_EXECUTION SET LAST_SUCCESS_DATE = NOW() - INTERVAL '%d HOURS', UPDATED_AT = NOW() WHERE API_KEY = '%s'",
|
||||||
|
targetSchema, lastExecutionBufferHours, getApiKey());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -87,11 +95,26 @@ public class RiskImportRangeJobConfig extends BaseMultiStepJobConfig<RiskDto, Ri
|
|||||||
@Override
|
@Override
|
||||||
protected Job createJobFlow(JobBuilder jobBuilder) {
|
protected Job createJobFlow(JobBuilder jobBuilder) {
|
||||||
return jobBuilder
|
return jobBuilder
|
||||||
.start(riskRangeImportStep()) // 1단계: API 데이터 적재
|
.start(riskRangeImportStep())
|
||||||
.next(riskLastExecutionUpdateStep()) // 2단계: 모두 완료 시, BATCH_LAST_EXECUTION 마지막 성공일자 업데이트
|
.next(riskEmptyResponseDecider())
|
||||||
|
.on("EMPTY_RESPONSE").end()
|
||||||
|
.from(riskEmptyResponseDecider()).on("*").to(riskLastExecutionUpdateStep())
|
||||||
|
.end()
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public JobExecutionDecider riskEmptyResponseDecider() {
|
||||||
|
return (jobExecution, stepExecution) -> {
|
||||||
|
if (stepExecution != null && stepExecution.getReadCount() == 0) {
|
||||||
|
log.info("[RiskRangeImportJob] Decider: EMPTY_RESPONSE - 응답 데이터 0건으로 LAST_EXECUTION 업데이트 스킵");
|
||||||
|
return new FlowExecutionStatus("EMPTY_RESPONSE");
|
||||||
|
}
|
||||||
|
log.info("[RiskRangeImportJob] Decider: NORMAL - LAST_EXECUTION 업데이트 진행");
|
||||||
|
return new FlowExecutionStatus("NORMAL");
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected ItemReader<RiskDto> createReader() {
|
protected ItemReader<RiskDto> createReader() {
|
||||||
return riskDataRangeReader;
|
return riskDataRangeReader;
|
||||||
@ -137,11 +160,11 @@ public class RiskImportRangeJobConfig extends BaseMultiStepJobConfig<RiskDto, Ri
|
|||||||
@Bean
|
@Bean
|
||||||
public Tasklet riskLastExecutionUpdateTasklet() {
|
public Tasklet riskLastExecutionUpdateTasklet() {
|
||||||
return (contribution, chunkContext) -> {
|
return (contribution, chunkContext) -> {
|
||||||
log.info(">>>>> 모든 스텝 성공: BATCH_LAST_EXECUTION 업데이트 시작");
|
log.info(">>>>> 모든 스텝 성공: BATCH_LAST_EXECUTION 업데이트 시작 (버퍼: {}시간)", lastExecutionBufferHours);
|
||||||
|
|
||||||
jdbcTemplate.execute(getBatchUpdateSql());
|
jdbcTemplate.execute(getBatchUpdateSql());
|
||||||
|
|
||||||
log.info(">>>>> BATCH_LAST_EXECUTION 업데이트 완료");
|
log.info(">>>>> BATCH_LAST_EXECUTION 업데이트 완료 (LAST_SUCCESS_DATE = NOW() - {}시간)", lastExecutionBufferHours);
|
||||||
return RepeatStatus.FINISHED;
|
return RepeatStatus.FINISHED;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@ -28,6 +28,9 @@ public class ShipDetailSyncJobConfig {
|
|||||||
@Value("${app.batch.target-schema.name}")
|
@Value("${app.batch.target-schema.name}")
|
||||||
private String targetSchema;
|
private String targetSchema;
|
||||||
|
|
||||||
|
@Value("${app.batch.last-execution-buffer-hours:24}")
|
||||||
|
private int lastExecutionBufferHours;
|
||||||
|
|
||||||
// API 키 정의 (배치 로그 관리용)
|
// API 키 정의 (배치 로그 관리용)
|
||||||
protected String getApiKey() {
|
protected String getApiKey() {
|
||||||
return "SHIP_DETAIL_SYNC_API";
|
return "SHIP_DETAIL_SYNC_API";
|
||||||
@ -36,8 +39,8 @@ public class ShipDetailSyncJobConfig {
|
|||||||
// 마지막 실행 일자 업데이트 SQL
|
// 마지막 실행 일자 업데이트 SQL
|
||||||
protected String getBatchUpdateSql() {
|
protected String getBatchUpdateSql() {
|
||||||
return String.format(
|
return String.format(
|
||||||
"UPDATE %s.BATCH_LAST_EXECUTION SET LAST_SUCCESS_DATE = NOW(), UPDATED_AT = NOW() WHERE API_KEY = '%s'",
|
"UPDATE %s.BATCH_LAST_EXECUTION SET LAST_SUCCESS_DATE = NOW() - INTERVAL '%d HOURS', UPDATED_AT = NOW() WHERE API_KEY = '%s'",
|
||||||
targetSchema, getApiKey()
|
targetSchema, lastExecutionBufferHours, getApiKey()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -139,11 +142,11 @@ public class ShipDetailSyncJobConfig {
|
|||||||
@Bean
|
@Bean
|
||||||
public Tasklet shipDetailSyncLastExecutionUpdateTasklet() {
|
public Tasklet shipDetailSyncLastExecutionUpdateTasklet() {
|
||||||
return (contribution, chunkContext) -> {
|
return (contribution, chunkContext) -> {
|
||||||
log.info(">>>>> 모든 테이블 동기화 성공: BATCH_LAST_EXECUTION 업데이트 시작");
|
log.info(">>>>> 모든 테이블 동기화 성공: BATCH_LAST_EXECUTION 업데이트 시작 (버퍼: {}시간)", lastExecutionBufferHours);
|
||||||
|
|
||||||
jdbcTemplate.execute(getBatchUpdateSql());
|
jdbcTemplate.execute(getBatchUpdateSql());
|
||||||
|
|
||||||
log.info(">>>>> BATCH_LAST_EXECUTION 업데이트 완료");
|
log.info(">>>>> BATCH_LAST_EXECUTION 업데이트 완료 (LAST_SUCCESS_DATE = NOW() - {}시간)", lastExecutionBufferHours);
|
||||||
return RepeatStatus.FINISHED;
|
return RepeatStatus.FINISHED;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@ -69,9 +69,15 @@ public class ShipDetailUpdateJobConfig extends BaseMultiStepJobConfig<ShipDetail
|
|||||||
@Value("${app.batch.ship-detail-update.max-retry-count:3}")
|
@Value("${app.batch.ship-detail-update.max-retry-count:3}")
|
||||||
private int maxRetryCount;
|
private int maxRetryCount;
|
||||||
|
|
||||||
|
@Value("${app.batch.last-execution-buffer-hours:24}")
|
||||||
|
private int lastExecutionBufferHours;
|
||||||
|
|
||||||
protected String getApiKey() {return "SHIP_DETAIL_UPDATE_API";}
|
protected String getApiKey() {return "SHIP_DETAIL_UPDATE_API";}
|
||||||
protected String getBatchUpdateSql() {
|
protected String getBatchUpdateSql() {
|
||||||
return String.format("UPDATE %s.BATCH_LAST_EXECUTION SET LAST_SUCCESS_DATE = NOW(), UPDATED_AT = NOW() WHERE API_KEY = '%s'", targetSchema, getApiKey());}
|
return String.format(
|
||||||
|
"UPDATE %s.BATCH_LAST_EXECUTION SET LAST_SUCCESS_DATE = NOW() - INTERVAL '%d HOURS', UPDATED_AT = NOW() WHERE API_KEY = '%s'",
|
||||||
|
targetSchema, lastExecutionBufferHours, getApiKey());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public ShipDetailUpdateJobConfig(
|
public ShipDetailUpdateJobConfig(
|
||||||
@ -114,6 +120,7 @@ public class ShipDetailUpdateJobConfig extends BaseMultiStepJobConfig<ShipDetail
|
|||||||
.start(ShipDetailUpdateStep())
|
.start(ShipDetailUpdateStep())
|
||||||
.next(retryModeDecider())
|
.next(retryModeDecider())
|
||||||
.on("RETRY").end()
|
.on("RETRY").end()
|
||||||
|
.from(retryModeDecider()).on("EMPTY_RESPONSE").end()
|
||||||
.from(retryModeDecider()).on("NORMAL").to(shipDetailLastExecutionUpdateStep())
|
.from(retryModeDecider()).on("NORMAL").to(shipDetailLastExecutionUpdateStep())
|
||||||
.end()
|
.end()
|
||||||
.build();
|
.build();
|
||||||
@ -131,6 +138,12 @@ public class ShipDetailUpdateJobConfig extends BaseMultiStepJobConfig<ShipDetail
|
|||||||
log.info("[ShipDetailUpdateJob] Decider: RETRY 모드 - LAST_EXECUTION 업데이트 스킵");
|
log.info("[ShipDetailUpdateJob] Decider: RETRY 모드 - LAST_EXECUTION 업데이트 스킵");
|
||||||
return new FlowExecutionStatus("RETRY");
|
return new FlowExecutionStatus("RETRY");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (stepExecution != null && stepExecution.getReadCount() == 0) {
|
||||||
|
log.info("[ShipDetailUpdateJob] Decider: EMPTY_RESPONSE - 응답 데이터 0건으로 LAST_EXECUTION 업데이트 스킵 (다음 실행 시 동일 범위 재조회)");
|
||||||
|
return new FlowExecutionStatus("EMPTY_RESPONSE");
|
||||||
|
}
|
||||||
|
|
||||||
log.info("[ShipDetailUpdateJob] Decider: NORMAL 모드 - LAST_EXECUTION 업데이트 진행");
|
log.info("[ShipDetailUpdateJob] Decider: NORMAL 모드 - LAST_EXECUTION 업데이트 진행");
|
||||||
return new FlowExecutionStatus("NORMAL");
|
return new FlowExecutionStatus("NORMAL");
|
||||||
};
|
};
|
||||||
@ -213,11 +226,11 @@ public class ShipDetailUpdateJobConfig extends BaseMultiStepJobConfig<ShipDetail
|
|||||||
@Bean
|
@Bean
|
||||||
public Tasklet shipDetailLastExecutionUpdateTasklet() {
|
public Tasklet shipDetailLastExecutionUpdateTasklet() {
|
||||||
return (contribution, chunkContext) -> {
|
return (contribution, chunkContext) -> {
|
||||||
log.info(">>>>> 모든 스텝 성공: BATCH_LAST_EXECUTION 업데이트 시작");
|
log.info(">>>>> 모든 스텝 성공: BATCH_LAST_EXECUTION 업데이트 시작 (버퍼: {}시간)", lastExecutionBufferHours);
|
||||||
|
|
||||||
jdbcTemplate.execute(getBatchUpdateSql());
|
jdbcTemplate.execute(getBatchUpdateSql());
|
||||||
|
|
||||||
log.info(">>>>> BATCH_LAST_EXECUTION 업데이트 완료");
|
log.info(">>>>> BATCH_LAST_EXECUTION 업데이트 완료 (LAST_SUCCESS_DATE = NOW() - {}시간)", lastExecutionBufferHours);
|
||||||
return RepeatStatus.FINISHED;
|
return RepeatStatus.FINISHED;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@ -110,6 +110,9 @@ app:
|
|||||||
enabled: true
|
enabled: true
|
||||||
cron: "0 0 * * * ?" # Every hour
|
cron: "0 0 * * * ?" # Every hour
|
||||||
|
|
||||||
|
# LAST_EXECUTION 버퍼 시간 (시간 단위) - 외부 DB 동기화 지연 대응
|
||||||
|
last-execution-buffer-hours: 24
|
||||||
|
|
||||||
# ShipDetailUpdate 배치 설정 (dev - 기존과 동일하게 20건 유지)
|
# ShipDetailUpdate 배치 설정 (dev - 기존과 동일하게 20건 유지)
|
||||||
ship-detail-update:
|
ship-detail-update:
|
||||||
batch-size: 20 # dev에서는 문제 없으므로 기존 20건 유지
|
batch-size: 20 # dev에서는 문제 없으므로 기존 20건 유지
|
||||||
|
|||||||
@ -112,6 +112,9 @@ app:
|
|||||||
enabled: true
|
enabled: true
|
||||||
cron: "0 0 * * * ?" # Every hour
|
cron: "0 0 * * * ?" # Every hour
|
||||||
|
|
||||||
|
# LAST_EXECUTION 버퍼 시간 (시간 단위) - 외부 DB 동기화 지연 대응
|
||||||
|
last-execution-buffer-hours: 24
|
||||||
|
|
||||||
# ShipDetailUpdate 배치 설정 (prod 튜닝)
|
# ShipDetailUpdate 배치 설정 (prod 튜닝)
|
||||||
ship-detail-update:
|
ship-detail-update:
|
||||||
batch-size: 10 # API 요청 당 IMO 건수 (프록시 타임아웃 방지)
|
batch-size: 10 # API 요청 당 IMO 건수 (프록시 타임아웃 방지)
|
||||||
|
|||||||
@ -165,6 +165,9 @@ app:
|
|||||||
enabled: true
|
enabled: true
|
||||||
cron: "0 0 * * * ?" # Every hour
|
cron: "0 0 * * * ?" # Every hour
|
||||||
|
|
||||||
|
# LAST_EXECUTION 버퍼 시간 (시간 단위) - 외부 DB 동기화 지연 대응
|
||||||
|
last-execution-buffer-hours: 24
|
||||||
|
|
||||||
# ShipDetailUpdate 배치 설정
|
# ShipDetailUpdate 배치 설정
|
||||||
ship-detail-update:
|
ship-detail-update:
|
||||||
batch-size: 10 # API 요청 당 IMO 건수
|
batch-size: 10 # API 요청 당 IMO 건수
|
||||||
|
|||||||
불러오는 중...
Reference in New Issue
Block a user