도입하게된 계기

<aside> 💡 기존 스케줄러로 현재 시간으로부터 예약된 스케줄링 또는 선행되는 스케줄링이 수행된 후에는 그 이후 스케줄링이 등록이 안되거나 수행이 안되어야 했고 이에 맞는 기술을 찾아보다 도입.

</aside>

작동 원리

Database setup

https://docs.spring.io/spring-boot/docs/2.1.9.RELEASE/reference/html/boot-features-quartz.html

By default, the database is detected and initialized by using the standard scripts provided with the Quartz library. These scripts drop existing tables, deleting all triggers on every restart. It is also possible to provide a custom script by setting the spring.quartz.jdbc.schema property.

기본적으로 Quartz 라이브러리와 함께 제공되는 표준 스크립트를 사용하여 데이터베이스가 감지되고 초기화됩니다. 이 스크립트들은 기존 테이블을 삭제하며, 모든 재시작 시 모든 트리거를 삭제합니다. spring.quartz.jdbc.schema 속성을 설정하여 사용자 지정 스크립트를 제공하는 것도 가능합니다.

위 이유로 quartz github repo들어가서 postgres사용시 사용되는 job_store ddl을 가져와 create table if not exist로 변경하고 매뉴얼로 직접 한번 생성해서 사용하고 있다:

https://github.com/quartz-scheduler/quartz/tree/main/quartz/src/main/resources/org/quartz/impl/jdbcjobstore

<aside> 💡 테이블을 직접 생성하여 기존 스케줄들이 삭제되지 않도록 한다! (auto-ddl 인 경우 테이블이 삭제되고 생성되어 등록된 스케줄이 삭제되는 이슈가 발생함)

</aside>

Configuration

package com.smatch.core.common.configuration

import org.quartz.spi.JobFactory
import org.quartz.spi.TriggerFiredBundle
import org.springframework.beans.factory.config.AutowireCapableBeanFactory
import org.springframework.beans.factory.config.PropertiesFactoryBean
import org.springframework.boot.autoconfigure.EnableAutoConfiguration
import org.springframework.boot.autoconfigure.quartz.QuartzDataSource
import org.springframework.boot.context.properties.ConfigurationProperties
import org.springframework.boot.jdbc.DataSourceBuilder
import org.springframework.context.ApplicationContext
import org.springframework.context.ApplicationContextAware
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.core.io.ClassPathResource
import org.springframework.scheduling.quartz.SchedulerFactoryBean
import org.springframework.scheduling.quartz.SpringBeanJobFactory
import java.util.Properties
import javax.sql.DataSource

@Configuration
@EnableAutoConfiguration
class QuartzConfig(
    private val applicationContext: ApplicationContext
) {
	
    @Bean
    fun schedulerFactory(): SchedulerFactoryBean {
        val factory = SchedulerFactoryBean()
        factory.setApplicationContext(applicationContext)
        factory.setJobFactory(jobFactory(applicationContext))
        factory.setDataSource(quartzDataSource())
        factory.setQuartzProperties(quartzProperties())
        factory.setOverwriteExistingJobs(true)
        return factory
    }

    @Bean
    @QuartzDataSource
    @ConfigurationProperties(prefix = "spring.datasource.quartz")
    fun quartzDataSource(): DataSource {
        return DataSourceBuilder.create().build()
    }

    @Bean
    fun quartzProperties(): Properties {
        val propertiesFactoryBean = PropertiesFactoryBean()
        propertiesFactoryBean.setLocations(ClassPathResource("/quartz/quartz.properties"))
        propertiesFactoryBean.afterPropertiesSet()
        return propertiesFactoryBean.`object`!!
    }

    @Bean
    fun jobFactory(applicationContext: ApplicationContext?): JobFactory {
        val jobFactory = AutowiringSpringBeanJobFactory()
        jobFactory.setApplicationContext(applicationContext!!)
        return jobFactory
    }
}

class AutowiringSpringBeanJobFactory : SpringBeanJobFactory(), ApplicationContextAware {
    private var beanFactory: AutowireCapableBeanFactory? = null

    override fun setApplicationContext(applicationContext: ApplicationContext) {
        beanFactory = applicationContext.autowireCapableBeanFactory
    }

    override fun createJobInstance(bundle: TriggerFiredBundle): Any {
        val job = super.createJobInstance(bundle)
        beanFactory!!.autowireBean(job)
        return job
    }
}