스프링 배치를 사용하여 매일 한번씩 카카오 계정 서버에서 휴면/탈퇴 계정 목록을 가져와 내가 담당하고 있는 서비스를 탈퇴 시키는 작업을 진행했고, 이에 대한 기록을 남긴다.
방법
배치 오류 발생시?
방법
배치 오류 발생시?
상세 에러 내용은 kibana에서 확인 (es에 서빙이 되도록 로그 작업 필요)
방법
배치 오류 발생시?
3번 사용 결정 이유
배치를 위한 파드(서버)를 계속 띄울 필요 없이, 배치가 수행되는 시간에만 파드를 띄울수 있다는 장점이 있다.
파트 내에 이미 kubernetes plugin 이 설치된 젠킨스가 있어서 세팅이 간단 했던것도 한몫함.
한 곳에서 (jenkins) 수행 로그(결과 또는 에러 로그) 확인 및 재수행(retry)을 진행할 수 있다는 장점이 있다.
때문에 비즈니스 DB와 spring batch DB를 분리하기로 결정.
spring:
jpa:
database: mysql
database-platform: org.hibernate.dialect.MySQL8Dialect
...@EnableConfigurationProperties(BatchProperties::class)
@Configuration
class BatchConfig {
// @Bean 없이 생성해야 primany datasource bean 은 mysql 이 된다.
private val batchDataSource =
EmbeddedDatabaseBuilder().setType(EmbeddedDatabaseType.H2)
.addScript("classpath:org/springframework/batch/core/schema-drop-h2.sql")
.addScript("classpath:/org/springframework/batch/core/schema-h2.sql")
.build()
// 메타 테이블 스키마에 대한 초기화를 진행한다. 해당 설정이 없으면 테이블 생성은 mysql에 생성된다.
@Bean
fun batchDataSourceInitializer(
resourceLoader: ResourceLoader?,
properties: BatchProperties?,
): BatchDataSourceInitializer =
BatchDataSourceInitializer(
batchDataSource,
resourceLoader,
properties,
)
// 배치설정를 진행하며, 여기서 설정된 datasource에 배치 메타 데이터에 대해 crud 가 발생
@Bean
fun batchConfigurer(): BatchConfigurer = DefaultBatchConfigurer(batchDataSource)
}DataSourceTransactionManager 를 사용하므로 JPA로 수행되는 쿼리도 해당 트랜잭션 매니저로 수행이 된다.DataSourceTransactionManager으로 수행이 되면 JpaTransaction이 생성되지 않아 기본 쿼리인 조회(select)만 수행이 가능하게 된다.@Configuration
class JpaConfig {
// datasource bean 은 mysql 이다.
@Bean
@Primary
fun jpaTransactionManager(dataSource: DataSource): JpaTransactionManager {
return JpaTransactionManager().apply { this.dataSource = dataSource }
}
}
class JobConfig {
@Bean
fun dormantStep(
jpaTransactionManager: JpaTransactionManager,
): Step {
return stepBuilderFactory["dormantStep"]
.tasklet(dormantUserTasklet)
.transactionManager(jpaTransactionManager)
.build()
}
@Bean
fun dormantJob(
dormantStep: Step,
jobCompletionListener: JobExecutionListener
): Job {
return jobBuilderFactory["dormantJob"]
.listener(jobCompletionListener)
.start(dormantStep)
.build()
}
}where 절로 인해 테이블 전체가 Lock이 걸리진 않을거라고 예상했는데… InnoDB에서 UPDATE또는 DELETE는 where 절과 상관없이 스캔된 모든 인덱스 레코드에 잠금을 건다. 수행 당시에는 조건에 걸린 필드에 인덱스가 없었기 때문에 모든 row에 락이 걸리게 된 것이다.
class ATestlet() : Tasklet {
override fun execute( {
...
loop (100) {
run()
}
}
}
class RunnerClass() {
@Transactional
fun run() {
...
}
}