지금껏 플젝을 진행하면서 SpringBoot의 cron Scheduler만 사용을 하다가, 특이한 요구사항(?)으로 인해 fixedDelay, fixedRate를 사용하게 되었다. 사용을 해보니 fixedDelay만을 사용하게 되었지만, 이 참에 정리해보고자 한다.

 

일단 fixedDelay와 fixedRate의 구현은 다음과 같이 진행한다.

 

1
2
3
4
5
6
7
8
9
10
11
12
import org.springframework.scheduling.annotation.Scheduled;
 
@Scheduled(fixedDelay = 1000)
public void fixedDelayScheduler() {
  logger.info("FixedDelay Start!");
  try {
    Thread.sleep(600);
  } catch {
    e.printStackTrace();
  }
  logger.info("FixedDelay End!");
}
cs

 

1
2
3
4
5
6
7
8
9
10
11
12
import org.springframework.scheduling.annotation.Scheduled;
 
@Scheduled(fixedRate = 1000)
public void fixedRateScheduler() {
  logger.info("FixedRate Start!");
  try {
    Thread.sleep(600);
  } catch {
    e.printStackTrace();
  }
  logger.info("FixedRate End!");
}
cs

사실 구현에 있어 fixedDelay , fixedRate의 차이는 존재하지 않는다. 다만, 어떻게 동작하느냐가 매우 다르므로 사용상에 유의할 필요가 있다.

 

먼저, fixedDelay는 현재 Schedule 상에 걸린 작업을 모두 끝난 이후에 설정된 시간이 카운팅되는 형태이며

fixedRate는 현재 Schedule 상에 걸린 작업의 완료 여부와 상관 없이 Scheduler가 시작한 시간으로부터 카운팅되는 형태이다.

 

위의 소스에 대해 로그를 살펴보자.

1
2
3
4
5
6
7
### Fixed Delay ###
2021-06-21 14:05:36.094  INFO 5536 --- [   DUMMY-1] com.example.DummyController   : FixedDelay Start!
2021-06-21 14:05:36.695  INFO 5536 --- [   DUMMY-1] com.example.DummyController   : FixedDelay END!
2021-06-21 14:05:37.698  INFO 5536 --- [   DUMMY-1] com.example.DummyController   : FixedDelay Start!
2021-06-21 14:05:38.300  INFO 5536 --- [   DUMMY-1] com.example.DummyController   : FixedDelay END!
2021-06-21 14:05:39.301  INFO 5536 --- [   DUMMY-1] com.example.DummyController   : FixedDelay Start!
2021-06-21 14:05:39.903  INFO 5536 --- [   DUMMY-1] com.example.DummyController   : FixedDelay END!
cs

 

1
2
3
4
5
6
7
### Fixed Rate ###
2021-06-21 14:06:42.762  INFO 5536 --- [   DUMMY-1] com.example.DummyController   : FixedRate Start!
2021-06-21 14:06:43.363  INFO 5536 --- [   DUMMY-1] com.example.DummyController   : FixedRate END!
2021-06-21 14:06:43.763  INFO 5536 --- [   DUMMY-1] com.example.DummyController   : FixedRate Start!
2021-06-21 14:06:44.365  INFO 5536 --- [   DUMMY-1] com.example.DummyController   : FixedRate END!
2021-06-21 14:06:44.765  INFO 5536 --- [   DUMMY-1] com.example.DummyController   : FixedRate Start!
2021-06-21 14:06:45.367  INFO 5536 --- [   DUMMY-1] com.example.DummyController   : FixedRate END!
cs

두 소스 모두 Start와 End 사이에 600ms의 Sleep을 주었다. 그 후에 End log가 찍히며, 이후 설정된 시간인 1000ms(1sec) 이후에 스케쥴러가 재실행되는 구조로 되어있다.

 

FixedDelay의 경우에는 14:05:36.094에 Start, 600ms Sleep 이후인 14:05:36:695에 End가 찍혔으며 End log가 찍힌 1000ms 이후인 14:05:37:698에 해당 스케쥴러가 실행되었다. 첫번째 Start 로그와 두번째 Start 로그의 시간차는 중간에 Thread.sleep으로 걸어준 600ms가 반영된 1600ms 만큼 차이가 존재한다.

 

FixedRate의 경우에는 14:06:42.762에 Start, 600ms Sleep 이후인 14:06:43:363에 End가 찍혔으나 그 시간과는 상관없이 Start 로그는 첫번째 Start 로그가 찍힌 시간에서 1000ms 이후인 14:06:43:763에 Start 로그가 찍혔다. 즉, 첫번째 Start 로그와 두번째 Start 로그의 시간차는 도중의 Thread.Sleep이 무시가 된 1000ms로 동일하다.

 

이는 스케쥴러 작업에 아주 중요한 영향을 끼친다. 내가 이번 프로젝트에서 fixedRate를 배제하고 fixedDelay를 사용하게 된 이유는 다음과 같다.

 

조건1. A 스케쥴러는 5분(300초)의 주기로 작동한다
조건2. A 스케쥴러에서 수행하는 작업은 짧게는 10초, 길게는 10분 이상이 소요된다
조건3. 조건2의 작업에서, DB의 Insert/Update 작업이 수시로 발생한다

위와 같은 작업상황에서 fixedRate로 수행을 할 경우, 조건 1의 10분정도 걸리는 작업을 수행하는 경우, 5분이 지난 시점에 동일한 A 스케쥴러가 작동을 하게 될 것이며, 같은 작업을 두 번 이상 수행하는 결과를 낳게 된다.

이는 의도된 행동과는 다르게 동작하기에, 동일한 데이터에 대해 한 번만 처리가 되어야 하는 작업을 여러번 처리하게 되므로 오작동을 하게 된다.

 

이에, 수행시간동안에는 스케쥴러가 동작하지 않고, 작업이 끝난 이후 설정된 시간 텀을 주는 fixedDelay를 사용하게 된 이유라고 볼 수 있다.

 

추가로, fixedDelayString , fixedRateString으로도 선언이 가능하며, 이는 long형 대신 String 형으로 선언을 받는다는 차이가 있다. application.yml에 선언하고 쓰기에 좋음ㅋ

블로그 이미지

김생선

세상의 모든것을 어장관리

,