Scheduling (Spring Boot)

Introduction

This page is about Spring scheduling.

It emphasizes how to achieve that a schedule method only runs in one of the application nodes, using Shedlock.


ShedLock works only in environments with a shared database by declaring a proper LockProvider. It creates a table or document in the database where it stores the information about the current locks.

ShedLock supports Mongo, Redis, Hazelcast, ZooKeeper, and anything with a JDBC driver.



References

https://github.com/lukas-krecan/ShedLock

https://www.baeldung.com/shedlock-spring

https://www.baeldung.com/spring-scheduled-tasks


@Scheduled syntax

Cron sample:

/** At 02:07:27am every day (UTC) */

@Scheduled(cron = "27 07 02 * * ?", zone = "UTC")


/** At 02:07:02am every day (Europe/Madrid) */

@Scheduled(cron = "2 7 2 * * ?", zone = "Europe/Madrid")

Fixed delay, w/ initial delay, sample:

@Scheduled(fixedDelay = 24 * 60 * 60 * 1000, initialDelay = 24 * 60 * 60 * 1000)


Step by step

The following sample was tested on a Spring Boot 2.6.0 application with OpenJDK 11, using both: H2 and PostgreSQL.

POM configuration (pom.xml)

<properties>

    <shedlock.version>4.44.0</shedlock.version>

</properties>

...

<!-- ShedLock: handle scheduler synchronization over multiple instances -->

<dependency>

<groupId>net.javacrumbs.shedlock</groupId>

<artifactId>shedlock-spring</artifactId>

<version>${shedlock.version}</version>

</dependency>

<!-- ShedLock: LockProvider JdbcTemplate -->

<dependency>

<groupId>net.javacrumbs.shedlock</groupId>

<artifactId>shedlock-provider-jdbc-template</artifactId>

<version>${shedlock.version}</version>

</dependency>


ShedLock database table

Create a database table for ShedLock to keep information about scheduler locks.

-Using SQL:

Sample syntax compatible with PostgreSQL DBMS:

CREATE TABLE shedlock(

  name VARCHAR(64) NOT NULL,

  lock_until TIMESTAMP NOT NULL,

  locked_at TIMESTAMP NOT NULL,

  locked_by VARCHAR(255) NOT NULL,

PRIMARY KEY (name));

-Using Liquibase:

databaseChangeLog:


  - changeSet:

      id: "021"

      author: thatsme

      failOnError: true

      comment: "Creation of table: shedlock"

      changes:

        - createTable:

            tableName: shedlock

            columns:                

             - column:

                  name: name

                  type: varchar(64)

                  constraints:

                    nullable: false

                    primaryKey: true

                  remarks: "Record id"

             - column:

                  name: lock_until

                  type: timestamp(3)

                  constraints:

                    nullable: false              

                  remarks: "-"

             - column:

                  name: locked_at

                  type: timestamp(3)

                  defaultValueComputed: current_timestamp(3)

                  constraints:

                    nullable: false              

                  remarks: "-"           

             - column:

                  name: locked_by

                  type: varchar(255)

                  constraints:

                    nullable: false              

                  remarks: "-"

    

 

ShedLockConfig.java

package edu.cou.myapp;


import java.time.Clock;

import java.time.ZoneId;


import javax.sql.DataSource;


import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;

import org.springframework.jdbc.core.JdbcTemplate;


import net.javacrumbs.shedlock.core.ClockProvider;

import net.javacrumbs.shedlock.core.LockProvider;

import net.javacrumbs.shedlock.provider.jdbctemplate.JdbcTemplateLockProvider;


/**

 * ShedLockConfig.<br>

 * <br>

 * a) It configures ShedLock<br>

 * <br>

 * Reference:<br>

 * https://github.com/lukas-krecan/ShedLock

 */

@Configuration

public class ShedLockConfig {


  /**

   * Configure ShedLock JDBC lock provider

   * 

   * @param dataSource -

   * @return -

   */

  @Bean

  LockProvider lockProvider(DataSource dataSource) {

    return new JdbcTemplateLockProvider(//

        JdbcTemplateLockProvider.Configuration.builder()//

            .withJdbcTemplate(new JdbcTemplate(dataSource))//

            .usingDbTime() // Works on Postgres, MySQL, MariaDb, MS SQL, Oracle, DB2, HSQL and H2

            .build());

  }


}


ScheduledConfig.java

import javax.sql.DataSource;

import net.javacrumbs.shedlock.core.LockProvider;

import net.javacrumbs.shedlock.spring.annotation.EnableSchedulerLock;

import net.javacrumbs.shedlock.spring.annotation.SchedulerLock;


@EnableScheduling

@EnableSchedulerLock(defaultLockAtLeastFor = "PT4S", defaultLockAtMostFor = "PT32S")

@Configuration

public class ScheduledConfig {


  @Scheduled(fixedDelay = 24 * 60 * 60 * 1000, initialDelay = 24 * 60 * 60 * 1000)

  @SchedulerLock(name = "appRequestClean")

  public void appRequestClean() {

     //whatever

  }


  @Scheduled(cron = "2 7 2 * * ?", zone = "Europe/Madrid")

  @SchedulerLock(name = "dailyNightProcess", lockAtLeastFor = "PT16M",

      lockAtMostFor = "PT16H")

  public void dailyNightProcess() {

     //whatever

  }


}