Spring Boot ์Šค์ผ€์ค„๋Ÿฌ ์ค‘๋ณต ์‹คํ–‰ ๋ฐฉ์ง€: ShedLock & Redis ์ ์šฉ ๊ฐ€์ด๋“œ

Spring ์Šค์ผ€์ค„๋Ÿฌ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ฐฐ์น˜ ์ž‘์—… ๋ฐ ์˜ˆ์•ฝ๋œ ์ž‘์—…์„ ์†์‰ฝ๊ฒŒ ํ•  ์ˆ˜ ์žˆ์ง€๋งŒ AWS ECS, EKS์™€ ๊ฐ™์€ ํด๋ผ์šฐ๋“œ ๊ธฐ๋ฐ˜ ๋ถ„์‚ฐ ์ปดํ“จํŒ… ํ™˜๊ฒฝ์—์„œ๋Š” ๋ฉ€ํ‹ฐ ์ธ์Šคํ„ด์Šค๋กœ ์„œ๋น„์Šค๋ฅผ ํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋งŽ์€๋ฐ ์ด๋Ÿฌํ•œ ๊ฒฝ์šฐ ๊ฐ ์ธ์Šคํ„ด์Šค์˜ ์Šค์ผ€์ค„ ํƒœ์Šคํฌ๋ฅผ ๋™๊ธฐํ™”ํ•  ์ˆ˜ ์—†์–ด์„œ ๊ฐ™์€ ํƒœ์Šคํฌ๊ฐ€ ์ค‘๋ณต ์‹คํ–‰์ด ๋˜์–ด ์˜ˆ๊ธฐ์น˜ ์•Š์€ ๋ฌธ์ œ๋ฅผ ๋งŒ๋‚  ์ˆ˜ ์žˆ๋‹ค.

ShedLock์€ ์ด๋Ÿฌํ•œ ์ค‘๋ณต ์‹คํ–‰ ๋ฌธ์ œ์— ๋Œ€ํ•ด์„œ ๊ฐ ์ธ์Šคํ„ด์Šค ๊ฐ„์˜ ์ž ๊ธˆ ์ฒ˜๋ฆฌ๋ฅผ ์ œ๊ณตํ•˜์—ฌ ํ•˜๋‚˜์˜ ์ธ์Šคํ„ด์Šค์—์„œ๋งŒ ํƒœ์Šคํฌ๋ฅผ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•œ๋‹ค. (ํƒœ์Šคํฌ์— ์ž ๊ธˆ ์ด๋ฆ„์„ ์ง€์ •ํ•˜์—ฌ ๋™์ผํ•œ ์ด๋ฆ„์— ๋Œ€ํ•ด์„œ ์ž ๊ธˆ์ด ๋™์ž‘ํ•˜๋ฏ€๋กœ ๋” ์ข์€ ์˜๋ฏธ๋กœ๋Š” ํƒœ์Šคํฌ ๊ฐ„์˜ ์ž ๊ธˆ ์ฒ˜๋ฆฌ๋ผ๊ณ  ํ•˜๋Š” ๊ฒŒ ๋งž์„ ๊ฒƒ ๊ฐ™๋‹ค.)

  • ShedLock์€ ํƒœ์Šคํฌ ๊ฐ„์˜ ๋ฝ ์ฒ˜๋ฆฌ๋ฅผ ์œ„ํ•ด์„œ MongoDB, JDBC, Redis, Hazelcast์™€ ๊ฐ™์€ ์™ธ๋ถ€ ์ €์žฅ์†Œ๋ฅผ ์ง€์›ํ•œ๋‹ค.
  • ShedLock์€ ๋ณ‘๋ ฌ๋กœ ์‹คํ–‰ํ•  ์ค€๋น„๊ฐ€ ๋˜์ง€ ์•Š์•˜์ง€๋งŒ ์•ˆ์ „ํ•˜๊ฒŒ ๋ฐ˜๋ณต ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๋Š” ์˜ˆ์•ฝ๋œ ์ž‘์—…์ด ์žˆ๋Š” ์ƒํ™ฉ์—์„œ ์‚ฌ์šฉํ•˜๋„๋ก ์„ค๊ณ„๋˜์—ˆ๋‹ค.
  • ์ž ๊ธˆ์€ ์‹œ๊ฐ„ ๊ธฐ๋ฐ˜์ด๋ฉฐ ShedLock์€ ๋…ธ๋“œ์˜ ์‹œ๊ฐ„์ด ์„œ๋กœ ๋™๊ธฐํ™” ๋˜์–ด ์žˆ๋‹ค๊ณ  ๊ฐ€์ •ํ•œ๋‹ค.

ShedLock ๊ตฌ์„ฑ

ShedLock์€ ์„ธ ํŒŒํŠธ๋กœ ๊ตฌ์„ฑ๋˜์–ด ์žˆ๋‹ค.

  • Core – locking mechanism
  • Integration – Spring AOP ๋˜๋Š” ์ˆ˜๋™ ์ฝ”๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜๊ณผ ํ†ตํ•ฉ
  • LockProvider – SQL DB, Mongo, Redis์™€ ๊ฐ™์€ ์™ธ๋ถ€ ํ”„๋กœ์„ธ์Šค๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ž ๊ธˆ์„ ์ œ๊ณต

Usage

Dependency

ShedLock ์„ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•œ ๋””ํŽœ๋˜์‹œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

<dependency>
    <groupId>net.javacrumbs.shedlock</groupId>
    <artifactId>shedlock-spring</artifactId>
    <version>5.10.0</version>
</dependency>
XML

Annotation

Spring์—์„œ ์Šค์ผ€์ค„ ์ž ๊ธˆ์„ ํ™œ์„ฑํ™”ํ•˜๋ ค๋ฉด @EnableSchedulerLock ์–ด๋…ธํ…Œ์ด์…˜์„ ์‚ฌ์šฉํ•œ๋‹ค.

@Configuration
@EnableScheduling
@EnableSchedulerLock(defaultLockAtMostFor = "10m")
class MySpringConfiguration {
    ...
}
Java

์Šค์ผ€์ค„ ์ž‘์—…(Task)์— @SchedulerLock ์–ด๋…ธํ…Œ์ด์…˜์„ ์ง€์ •ํ•œ๋‹ค.

@Scheduled(...)
@SchedulerLock(name = "scheduledTaskName")
public void scheduledTask() {
    ...
    ...
}
Java

SchedulerLock ์–ด๋…ธํ…Œ์ด์…˜์—๋Š” ๋‹ค์Œ์˜ ์†์„ฑ์ด ์žˆ๋‹ค.

  • name
    • ๊ณ ์œ ํ•œ ์ด๋ฆ„์ด ์‚ฌ์šฉ๋˜์–ด์•ผ ํ•œ๋‹ค.
    • ๋™์ผํ•œ ์ด๋ฆ„์˜ ํƒœ์Šคํฌ๋Š” ์—ฌ๋Ÿฌ ์ธ์Šคํ„ด์Šค์— ๋Œ€ํ•ด์„œ ์ž ๊ธˆ์ฒ˜๋ฆฌ๋กœ ์ธํ•ด ํ•œ ๋ฒˆ๋งŒ ์ˆ˜ํ–‰๋œ๋‹ค.
  • lockAtLeastFor
    • ์ž ๊ธˆ์ด ์œ ์ง€๋˜์–ด์•ผ ํ•˜๋Š” ์ตœ์†Œ ์‹œ๊ฐ„์„ ์ง€์ •ํ•œ๋‹ค.
    • Task ์ž‘์—…์ด ์™„๋ฃŒ๋œ ์‹œ๊ฐ„๋ณด๋‹ค ์ง€์ •๋œ ์‹œ๊ฐ„์ด ๊ธธ๋ฉด ์ง€์ •๋œ ์‹œ๊ฐ„ ๋™์•ˆ lock ๋œ๋‹ค.
    • Task ์ž‘์—… ์†Œ์š” ์‹œ๊ฐ„์ด ์ง€์ •๋œ ์‹œ๊ฐ„๋ณด๋‹ค ๊ธธ๋ฉด lockAtMostFor์— ์ง€์ •๋œ ์‹œ๊ฐ„๊นŒ์ง€ ์ž ๊ธˆ์ด ์œ ์ง€๋˜์ง€๋งŒ lockAtMostFor duration ์ด์ „์— ์ž‘์—…์ด ์™„๋ฃŒ๋˜๋ฉด ์ž ๊ธˆ์€ ํ•ด์ œ๋œ๋‹ค.
  • lockAtMostFor
    • ์‹คํ–‰ ๋…ธ๋“œ๊ฐ€ ์ฃฝ์—ˆ์„ ๋•Œ ์ž ๊ธˆ์„ ์–ผ๋งˆ๋‚˜ ์˜ค๋ž˜ ์œ ์ง€ํ•ด์•ผ ํ•˜๋Š”์ง€ ์ง€์ •ํ•˜๋Š” ์†์„ฑ์ด๋‹ค.
    • ์ •์ƒ์ ์ธ ์ƒํ™ฉ์—์„œ๋Š” Task ์ž‘์—…์ด ์™„๋ฃŒ๋˜๋Š” ์ฆ‰์‹œ ์ž ๊ธˆ์ด ํ•ด์ œ๋œ๋‹ค.
    • lockAtLeastFor ์†์„ฑ์ด ์ง€์ •๋˜์ง€ ์•Š์€ ๊ฒฝ์šฐ ์ •์ƒ์ ์ธ ์‹คํ–‰ ์‹œ๊ฐ„๋ณด๋‹ค ํ›จ์”ฌ ๊ธด ๊ฐ’์œผ๋กœ lockAtMost ์†์„ฑ์„ ์ง€์ •ํ•ด์•ผ ํ•œ๋‹ค.

lockAtLeastFor ๊ฐ’์ด lockAtMostFor ์„ค์ •๊ฐ’๋ณด๋‹ค ํฐ ๊ฒฝ์šฐ ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•˜๋‹ˆ ์ฃผ์˜๊ฐ€ ํ•„์š”ํ•˜๋‹ค.

LockProvider Redis

Lock ๋ฐ์ดํ„ฐ ์ €์žฅ์†Œ๋ฅผ Redis๋ฅผ ์‚ฌ์šฉํ•  ๊ฒƒ์ด๋‹ค. ๊ทธ ์™ธ ์ €์žฅ์†Œ์— ๋Œ€ํ•ด์„œ๋Š” ํฌ์ŠคํŒ… ํ•˜๋‹จ์— ์žˆ๋Š” ์ฐธ๊ณ ๋งํฌ๋ฅผ ํ™•์ธํ•ด ๋ณด๊ธฐ ๋ฐ”๋ž€๋‹ค.
LockProvider์— ๋Œ€ํ•œ ๋””ํŽœ๋˜์‹œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

<dependency>
    <groupId>net.javacrumbs.shedlock</groupId>
    <artifactId>shedlock-provider-redis-spring</artifactId>
    <version>5.10.0</version>
</dependency>
XML

LockProvider์— RedisConnectionFactory ๋ฅผ ์ „๋‹ฌํ•˜๊ธฐ ์œ„ํ•œ ๊ตฌ์„ฑ์ด ํ•„์š”ํ•˜๋‹ค.

import net.javacrumbs.shedlock.provider.redis.spring.RedisLockProvider;
import org.springframework.data.redis.connection.RedisConnectionFactory;

...

@Bean
public LockProvider lockProvider(RedisConnectionFactory connectionFactory) {
    return new RedisLockProvider(connectionFactory, ENV);
}
Java

application.yml(. properties)์— Redis์— ๋Œ€ํ•œ ์—ฐ๊ฒฐ ์„ค์ •์ด ๋˜์–ด ์žˆ๋‹ค๋ฉด RedisConnectionFactory๋Š” ์ž๋™ ๊ตฌ์„ฑ์— ์˜ํ•ด์„œ ๋นˆ์œผ๋กœ ์ž๋™ ๋“ฑ๋ก๋œ๋‹ค.
RedisLockProvider ํด๋ž˜์Šค์˜ ์ƒ์„ฑ์ž๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

public RedisLockProvider(@NonNull RedisConnectionFactory redisConn) {
    this(redisConn, "default");
}

public RedisLockProvider(@NonNull RedisConnectionFactory redisConn, @NonNull String environment) {
    this(redisConn, environment, "job-lock");
}

public RedisLockProvider(@NonNull RedisConnectionFactory redisConn, @NonNull String environment, @NonNull String keyPrefix) {
    this(new StringRedisTemplate(redisConn), environment, keyPrefix);
}
...
Java

Redis์— Lock ๋ฐ์ดํ„ฐ๊ฐ€ ์ €์žฅ๋  ๋•Œ Key ํ˜•์‹์€ ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.
{key-prefix}:{environment}:{SchedulerLock-name}
RedisLockProvider ํด๋ž˜์Šค์—์„œ keyPrefix, environment ๊ฐ€ ์ „๋‹ฌ๋˜์ง€ ์•Š์„ ๊ฒฝ์šฐ์—๋Š” ๋””ํดํŠธ๋กœ ๋‹ค์Œ ๊ฐ’์ด ์‚ฌ์šฉ๋œ๋‹ค.

  • keyPrefix default : job-lock
  • environment default : default

๊ธฐ๊ฐ„ ์„ค์ • ํ˜•์‹

๊ด€๋ จ ์–ด๋…ธํ…Œ์ด์…˜์— ์ง€์ •๋˜๋Š” duration ์†์„ฑ ํ˜•์‹์€ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ํ˜•์‹์ด ์ง€์›๋œ๋‹ค.

  • duration + unit – ex) 1s, 5ms, 5m, 1d (4.0.0๋ถ€ํ„ฐ)
  • duration in ms – ex) 100 (Spring integration ๋งŒ)
  • ISO-8601 – ex) PT15M
Examples:

    "PT20.345S" -- parses as "20.345 seconds"
    "PT15M"     -- parses as "15 minutes" (where a minute is 60 seconds)
    "PT10H"     -- parses as "10 hours" (where an hour is 3600 seconds)
    "P2D"       -- parses as "2 days" (where a day is 24 hours or 86400 seconds)
    "P2DT3H4M"  -- parses as "2 days, 3 hours and 4 minutes"
    "P-6H3M"    -- parses as "-6 hours and +3 minutes"
    "-P6H3M"    -- parses as "-6 hours and -3 minutes"
    "-P-6H+3M"  -- parses as "+6 hours and -3 minutes"
Java

ShedLock + Redis ์ƒ˜ํ”Œ ์ฝ”๋“œ

ShedLock์„ ์ด์šฉํ•˜์—ฌ ๋‘ ๊ฐœ์˜ Task๊ฐ€ ํ•œ ํƒ€์ž„์— ํ•œ ๋ฒˆ์”ฉ ์‹คํ–‰ํ•˜๋„๋ก ๊ฐ„๋‹จํ•œ ์ƒ˜ํ”Œ์ฝ”๋“œ๋ฅผ ๊ตฌํ˜„ํ•ด ๋ณด์•˜๋‹ค.

  • spring boot docker compose๋ฅผ ์ด์šฉํ•˜์—ฌ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์‹คํ–‰ ์‹œ redis๋ฅผ docker์— ์˜ฌ๋ฆฌ๋„๋ก ๊ตฌ์„ฑํ•˜์˜€๋‹ค. spring boot docker compose์— ๋Œ€ํ•œ ๋‚ด์šฉ์€ ์•„๋ž˜ ํฌ์ŠคํŒ…์„ ์ฐธ๊ณ ํ•˜๊ธฐ ๋ฐ”๋ž€๋‹ค.
  • spring boot docker compose๋ฅผ ์‚ฌ์šฉํ•˜๋ฏ€๋กœ ๋กœ์ปฌ์— docker ์—”์ง„์ด ๊ตฌ๋™๋˜์–ด ์žˆ์–ด์•ผ ํ•œ๋‹ค.
  • docker compose CLI๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์–ด์•ผ ํ•œ๋‹ค.

spring boot docker compose support ๋กœ์ปฌ์—์„œ ์ธํ”„๋ผ ์˜ฌ๋ ค์„œ ํ…Œ์ŠคํŠธ ํ•˜๊ธฐ (spring boot 3.1)

dependency

ShedLock + Redis๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•œ dependency๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
    <groupId>net.javacrumbs.shedlock</groupId>
    <artifactId>shedlock-provider-redis-spring</artifactId>
    <version>5.10.0</version>
</dependency>
<dependency>
    <groupId>net.javacrumbs.shedlock</groupId>
    <artifactId>shedlock-spring</artifactId>
    <version>5.10.0</version>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-docker-compose</artifactId>
    <scope>runtime</scope>
    <optional>true</optional>
</dependency>
XML

spring boot docker compose๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด์„œ spring-boot-docker-compose๋ฅผ ๋””ํŽœ๋˜์‹œ๋กœ ์ถ”๊ฐ€ํ•˜์˜€๋‹ค.
start.spring.io์—์„œ ๋””ํŽœ๋˜์‹œ๋กœ spring-boot-docker-compose์™€ spring-boot-starter-data-redis ๋””ํŽœ๋˜์‹œ๋ฅผ ํฌํ•จํ•˜๋ฉด ์ž๋™์œผ๋กœ compose.yaml ํŒŒ์ผ์„ ์ƒ์„ฑํ•ด ์ค€๋‹ค. ๋‹ค์Œ์€ ์ž๋™ ์ƒ์„ฑ๋œ compose.yaml ๋‚ด์šฉ์ด๋‹ค.

services:
  redis:
    image: 'redis:latest'
    ports:
      - '6379'
YAML

Scheduler

๋งค๋ถ„๋งˆ๋‹ค ์‹คํ–‰๋˜๋Š” ๋‘ ๊ฐœ์˜ Task๋ฅผ ์ •์˜ํ•˜์˜€๋‹ค.

@Service
@EnableScheduling
@Slf4j
public class MyScheduler {

    @Scheduled(cron = "0 * * * * *")
    @SchedulerLock(name = "perMinuteScheduler", lockAtLeastFor = "50s", lockAtMostFor = "55s")
    public void task1() {
        log.info("task1 run");
    }

    @Scheduled(cron = "0 * * * * *")
    @SchedulerLock(name = "perMinuteScheduler", lockAtLeastFor = "50s", lockAtMostFor = "55s")
    public void task2() {
        log.info("task2 run");
    }
}
Java

์‹ค์ œ๋กœ๋Š” ๋‘ ๊ฐœ์˜ ์ปจํ…Œ์ด๋„ˆ ์ธ์Šคํ„ด์Šค๋กœ ํ…Œ์ŠคํŠธ๋ฅผ ํ•ด๋ด์•ผ๊ฒ ์ง€๋งŒ ์—ฌ๊ฑด์ƒ ๋™์ผํ•œ ShedLock name (perMinuteScheduler)์„ ๊ฐ€์ง„ ๋‘ ๊ฐœ์˜ Task๋ฅผ ๊ฐ€์ง€๊ณ  ์‹œํ—˜ํ•ด ๋ณด์•˜๋‹ค.

LockProvider Configuration

Lock ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•  LockProvider ๊ตฌ์„ฑ์„ ์ •์˜ํ•ด์•ผ ํ•œ๋‹ค.

@Configuration
@EnableSchedulerLock(defaultLockAtMostFor = "PT1H")
public class ShedLockConfig {

    @Bean
    public LockProvider lockProvider(RedisConnectionFactory connectionFactory) {
        return new RedisLockProvider(connectionFactory);
    }
}
Java

@EnableSchedulerLock(defaultLockAtMostFor = “PT1H”)์—์„œ defaultLockAtMostFor ์†์„ฑ์€ @SchedulerLock ์–ด๋…ธํ…Œ์ด์…˜์˜ lockAtMostFor ์–ด๋…ธํ…Œ์ด์…˜์ด ์ง€์ •๋˜์ง€ ์•Š์„ ๊ฒฝ์šฐ ์ ์šฉ๋  ๋””ํดํŠธ duration์ด๋‹ค.
Redis๋ฅผ ์ž ๊ธˆ ๋ฐ์ดํ„ฐ ์ €์žฅ์†Œ๋กœ ์‚ฌ์šฉํ•  ๊ฒƒ์ด๋ฏ€๋กœ RedisLockProvider๋ฅผ ๋นˆ์œผ๋กœ ๋“ฑ๋กํ•ด์•ผ ํ•œ๋‹ค. RedisConnectionFactory๋Š” Spring Boot Auto Configuration์— ์˜ํ•ด์„œ ์ž๋™ ์ฃผ์ž…๋œ๋‹ค.

ShedLock logging

ShedLock ๋™์ž‘ ํ™•์ธ์„ ์œ„ํ•ด์„œ net.javacrumbs.shedlock ํŒจํ‚ค์ง€๋ฅผ debug ๋ ˆ๋ฒจ๋กœ ์„ค์ •ํ•˜์˜€๋‹ค.

application.yml

logging:
  level:
    net.javacrumbs.shedlock: debug
YAML

์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์‹คํ–‰

์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์‹คํ–‰ํ•˜๋ฉด spring boot docker compose๋ฅผ ํ†ตํ•ด์„œ compose.yaml ์ด docker compose์— ์˜ํ•ด์„œ ์‹คํ–‰๋œ๋‹ค.

INFO 10030 --- [utReader-stderr] o.s.boot.docker.compose.core.DockerCli   : Container spring-shedlock-redis-1  Created
INFO 10030 --- [utReader-stderr] o.s.boot.docker.compose.core.DockerCli   : Container spring-shedlock-redis-1  Starting
INFO 10030 --- [utReader-stderr] o.s.boot.docker.compose.core.DockerCli   : Container spring-shedlock-redis-1  Started
INFO 10030 --- [utReader-stderr] o.s.boot.docker.compose.core.DockerCli   : Container spring-shedlock-redis-1  Waiting
INFO 10030 --- [utReader-stderr] o.s.boot.docker.compose.core.DockerCli   : Container spring-shedlock-redis-1  Healthy
Plaintext

๋กœ๊ทธ๋ฅผ ์‚ดํŽด๋ด…๋ฉด task1๊ณผ task2๊ฐ€ ๋งค๋ถ„๋งˆ๋‹ค ๋‘˜ ์ค‘์— ํ•œ ๋ฒˆ๋งŒ ์‹คํ–‰๋จ์„ ์•Œ ์ˆ˜ ์žˆ๋‹ค.

//task2๊ฐ€ lock์„ ํš๋“ํ•˜์—ฌ ์‹คํ–‰ํ•˜๋ฉฐ, ์ž ๊ธˆ์ด ์œ ์ง€๋  ์ˆ˜ ์žˆ๋Š” ์ตœ๋Œ€ ์‹œ๊ฐ„๋„ ํ‘œ์‹œ๋œ๋‹ค. (lockAtMostFor 55์ดˆ)
2023-11-20T22:56:00.005+09:00 DEBUG : Locked 'perMinuteScheduler', lock will be held at most until 2023-11-20T13:56:55.001Z
2023-11-20T22:56:00.006+09:00  INFO : task2 run
//์ž‘์—…์ด ์™„๋ฃŒ๋˜์—ˆ๊ณ  lockAtLeastFor์— ์˜ํ•ด์„œ 50์ดˆ๊ฐ„ lock์€ ์œ ์ง€๋จ์„ ์•Œ ์ˆ˜ ์žˆ๋‹ค.
2023-11-20T22:56:00.009+09:00 DEBUG : Task finished, lock 'perMinuteScheduler' will be released at 2023-11-20T13:56:50.001Z
//task1์€ lock์„ ํš๋“ํ•˜์ง€ ๋ชปํ•˜์—ฌ ์‹คํ–‰๋˜์ง€ ์•Š๋Š”๋‹ค.
2023-11-20T22:56:00.012+09:00 DEBUG : Not executing 'perMinuteScheduler'. It's locked.
...
//task1์ด lock์„ ํš๋“ํ•˜์—ฌ ์‹คํ–‰ํ•˜๋ฉฐ, ์ž ๊ธˆ์ด ์œ ์ง€๋  ์ˆ˜ ์žˆ๋Š” ์ตœ๋Œ€ ์‹œ๊ฐ„๋„ ํ‘œ์‹œ๋œ๋‹ค. (lockAtMostFor 55์ดˆ)
2023-11-20T23:08:00.007+09:00 DEBUG : Locked 'perMinuteScheduler', lock will be held at most until 2023-11-20T14:08:55.002Z
2023-11-20T23:08:00.007+09:00  INFO : task1 run
//์ž‘์—…์ด ์™„๋ฃŒ๋˜์—ˆ๊ณ  lockAtLeastFor์— ์˜ํ•ด์„œ 50์ดˆ๊ฐ„ lock์€ ์œ ์ง€๋จ์„ ์•Œ ์ˆ˜ ์žˆ๋‹ค.
2023-11-20T23:08:00.010+09:00 DEBUG : Task finished, lock 'perMinuteScheduler' will be released at 2023-11-20T14:08:50.002Z
//task2๋Š” lock์„ ํš๋“ํ•˜์ง€ ๋ชปํ•˜์—ฌ ์‹คํ–‰๋˜์ง€ ์•Š๋Š”๋‹ค.
2023-11-20T23:08:00.016+09:00 DEBUG : Not executing 'perMinuteScheduler'. It's locked.
...
Plaintext

redis-cli๋ฅผ ํ†ตํ•ด์„œ lock ๋ฐ์ดํ„ฐ๊ฐ€ redis์— ์ €์žฅ๋œ ๊ฒƒ์„ ํ™•์ธํ•ด ๋ณด์ž.

  • ๋กœ๊ทธ ๊ธฐ๋ก ์‹œ๊ฐ„์€ KST ์‹œ๊ฐ„์ด์ง€๋งŒ Redis์— ๊ธฐ๋ก๋œ ์‹œ๊ฐ„์€ UTC์ž„์„ ์ฃผ์˜ํ•˜์ž.
  • ์ž ๊ธˆ ๋ฐ์ดํ„ฐ Key๋Š” {key-prefix}:{environment}:{SchedulerLock-name} ํฌ๋งท์œผ๋กœ ์ƒ์„ฑ๋˜๋ฏ€๋กœ job-lock:default:preMinuteScheduler ์ด๋ฆ„์œผ๋กœ ์ €์žฅ๋˜์—ˆ์Œ์„ ์•Œ ์ˆ˜ ์žˆ๋‹ค.
  • ์ž ๊ธˆ ๋ฐ์ดํ„ฐ๋Š” ์ž ๊ธˆ์ด ํ•ด์ œ๋˜๋ฉด ์‚ญ์ œ๋œ๋‹ค.

lockAtLeastFor ์†์„ฑ์„ ์ง€์ •ํ•˜์ง€ ์•Š์œผ๋ฉด ๋””ํดํŠธ PT0S๊ฐ€ ์ ์šฉ๋œ๋‹ค.
-> lockAtLeastFor ์†์„ฑ์„ ์ง€์ •ํ•˜์ง€ ์•Š์œผ๋ฉด ํƒœ์Šคํฌ ์ž‘์—…์ด ์™„๋ฃŒ๋˜๋ฉด lock์€ ํ•ด์ œ๋œ๋‹ค.

๋งˆ์น˜๋ฉฐ

ShedLock์„ ๋„์ž…ํ•จ์œผ๋กœ์จ ๋ถ„์‚ฐ ํ™˜๊ฒฝ์—์„œ ์Šค์ผ€์ค„๋Ÿฌ ์ค‘๋ณต ์‹คํ–‰ ๋ฌธ์ œ๋ฅผ ๊ฐ„๋‹จํ•˜๊ฒŒ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋‹ค. ๋งŒ์•ฝ ๋” ๋ณต์žกํ•œ Job ๊ด€๋ฆฌ๊ฐ€ ํ•„์š”ํ•˜๋‹ค๋ฉด Spring Batch๋‚˜ Quartz๋ฅผ ๊ณ ๋ คํ•ด ๋ณด๋Š” ๊ฒƒ๋„ ์ข‹๊ฒ ๋‹ค.
์ „์ฒด ์ƒ˜ํ”Œ ์ฝ”๋“œ๋Š” gitlab ์—์„œ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.


์ฐธ๊ณ ๋งํฌ
https://github.com/lukas-krecan/ShedLock