나는 개인 싱글서버로 웹서버를 개발중이다.
그런데 어제 스케줄 실행에서 로그를 기록하는 것을 서버에 적용한 후 스케줄이 중복 실행되는 걸 발견했다.
큰 이상을 유발할 수 있는 상황은 아니었으나 그래도 깔끔하지 못하니 수정하고 싶었다.
검색을 해보니 Tomcat, spring 설정의 문제를 확인해 보라는 내용이 있었지만, 내 경우는 적용할 수 가 없었다.
(이건 내 지식이 얕기 때문에 이해를 제대로 하지 못해 적용하지 못했을 것이다.)
추가로 검색을 해보니 멀티 서버의 경우에서 스케줄 중복을 방지하는 방법을 발견했다.
넓게 본다면 비슷한 의도로 중복실행 방지를 가능하게 처리하는 방법이 될 것으로 생각하고 적용 했으며, 잘 반영 되었다.
* 참고한 좋은글
* 내 서버 환경 : spring boot 2.4.3, 스케줄 적용 중
자세한건 위 글을 참고하시고, 내 기준에서 추가로 보강한 내용만 적는다.
1. 테이블 생성 (mysql)
1
2
3
4
5
6
7
8
|
CREATE TABLE IF NOT EXISTS `bookkeeping`.`SHEDLOCK` (
`NAME` VARCHAR(64) NOT NULL COMMENT '스케줄잠금이름',
`LOCK_UNTIL` TIMESTAMP(3) NULL COMMENT '잠금기간',
`LOCKED_AT` TIMESTAMP(3) NULL COMMENT '잠금일시',
`LOCKED_BY` VARCHAR(255) NULL COMMENT '잠금신청자',
PRIMARY KEY (`NAME`))
ENGINE = InnoDB
COMMENT = '스케줄 잠금'
|
cs |
2. Bean 주입
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;
import net.javacrumbs.shedlock.core.LockProvider;
import net.javacrumbs.shedlock.provider.jdbctemplate.JdbcTemplateLockProvider;
@Configuration
public class SchedulerConfig {
@Bean
public LockProvider lockProvider(JdbcTemplate dataSource) {
return new JdbcTemplateLockProvider(dataSource);
}
}
|
cs |
이상.
오늘도 잘 해결해서 보람차다. ㅠㅠ
3. 21.11.24 추가 이슈.
스케줄 실행시 언제나 에러가 난다는 것이다.
스케줄의 name 값으로 select를 먼저하고, 확인해서 insert, update로 분기해서 진행해야 하는데 무조건 insert 후 에러가 나면 update를 하는거다.(어쨌든 에러가 나도 실행은 잘 되긴 한다.)
하지만 이 excption이 눈에 거슬려서 고쳐보려 했지만 해결 방법을 아직 찾지는 못했다.
* 에러 전문
1. INSERT INTO shedlock(name, lock_until, locked_at, locked_by) VALUES('job_10min', '11/22/2021 12:28:00.003', '11/22/2021 12:20:00.050', 'DESKTOP-VMFC4B1')
{FAILED after 16 msec}
java.sql.SQLIntegrityConstraintViolationException: Duplicate entry 'job_10min' for key 'PRIMARY'
at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:117)
at com.mysql.cj.jdbc.exceptions.SQLExceptionsMapping.translateException(SQLExceptionsMapping.java:122)
at com.mysql.cj.jdbc.ClientPreparedStatement.executeInternal(ClientPreparedStatement.java:953)
at com.mysql.cj.jdbc.ClientPreparedStatement.executeUpdateInternal(ClientPreparedStatement.java:1092)
at com.mysql.cj.jdbc.ClientPreparedStatement.executeUpdateInternal(ClientPreparedStatement.java:1040)
at com.mysql.cj.jdbc.ClientPreparedStatement.executeLargeUpdate(ClientPreparedStatement.java:1347)
at com.mysql.cj.jdbc.ClientPreparedStatement.executeUpdate(ClientPreparedStatement.java:1025)
at net.sf.log4jdbc.sql.jdbcapi.PreparedStatementSpy.executeUpdate(PreparedStatementSpy.java:1080)
at com.zaxxer.hikari.pool.ProxyPreparedStatement.executeUpdate(ProxyPreparedStatement.java:61)
at com.zaxxer.hikari.pool.HikariProxyPreparedStatement.executeUpdate(HikariProxyPreparedStatement.java)
at org.springframework.jdbc.core.JdbcTemplate.lambda$update$2(JdbcTemplate.java:965)
at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:651)
at org.springframework.jdbc.core.JdbcTemplate.update(JdbcTemplate.java:960)
at org.springframework.jdbc.core.JdbcTemplate.update(JdbcTemplate.java:981)
at org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate.update(NamedParameterJdbcTemplate.java:328)
at org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate.update(NamedParameterJdbcTemplate.java:333)
at net.javacrumbs.shedlock.provider.jdbctemplate.JdbcTemplateStorageAccessor.lambda$insertRecord$0(JdbcTemplateStorageAccessor.java:65)
at org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:140)
at net.javacrumbs.shedlock.provider.jdbctemplate.JdbcTemplateStorageAccessor.insertRecord(JdbcTemplateStorageAccessor.java:63)
at net.javacrumbs.shedlock.support.StorageBasedLockProvider.doLock(StorageBasedLockProvider.java:81)
at net.javacrumbs.shedlock.support.StorageBasedLockProvider.lock(StorageBasedLockProvider.java:65)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:344)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:208)
at com.sun.proxy.$Proxy61.lock(Unknown Source)
at net.javacrumbs.shedlock.core.DefaultLockingTaskExecutor.executeWithLock(DefaultLockingTaskExecutor.java:63)
at net.javacrumbs.shedlock.spring.aop.MethodProxyScheduledLockAdvisor$LockingInterceptor.invoke(MethodProxyScheduledLockAdvisor.java:85)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:750)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:692)
at packageName.core.schedule.cron.ScheduleCron$$EnhancerBySpringCGLIB$$5253d941.job_10min()
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at org.springframework.scheduling.support.ScheduledMethodRunnable.run(ScheduledMethodRunnable.java:84)
at org.springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:54)
at org.springframework.scheduling.concurrent.ReschedulingRunnable.run(ReschedulingRunnable.java:95)
at java.util.concurrent.Executors$RunnableAdapter.call(Unknown Source)
at java.util.concurrent.FutureTask.run(Unknown Source)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(Unknown Source)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)