Spring Top – Temp

Get started with Spring 5 and Spring Boot 2, through the reference Learn Spring course:

>> LEARN SPRING
Lightrun – Third Party Code
announcement - icon

Flakiness in REST requests is a common issue. A request can get a 200 OK in one scenario and a 409 next time. Sometimes a request can even succeed and fail intermittently on the same exact request. In short, working over HTTP can be a bit of a mess without solid tooling.

Also, while it’s easy enough to debug these issues locally when developing the application, we’re talking about production here - we can’t afford the downtime while you’re stepping in and out of code. Uptime is kind of the whole point.

With Lightrun, you can get the same level of access you get with a local debugger or profiler - no downtime required. You can add logs, metrics, and snapshots (think breakpoints, but without stopping the running service), in a safe and read-only manner - without redeploying, restarting, or even stopping the running service. Performance and security are maintained throughout the process.

Learn how to debug a live REST API (built with Spring, of course), using Lightrun, in this 5-minute tutorial:

>> Debugging REST Requests in Spring-Based applications using the Lightrun Platform

1. Overview

Spring provides an easy way to implement API for scheduling jobs. It works great until we deploy multiple instances of our application.

Spring, by default, cannot handle scheduler synchronization over multiple instances. It executes the jobs simultaneously on every node instead.

In this short tutorial, we'll look at ShedLock — a Java library that makes sure our scheduled tasks run only once at the same time and is an alternative to Quartz.

2. Maven Dependencies

To use ShedLock with Spring, we need to add the shedlock-spring dependency:

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

3. Configuration

Note that 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.

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

For this example, we'll use an in-memory H2 database.

To make it work, we need to provide the H2 database and the ShedLock JDBC dependency:

<dependency>
    <groupId>net.javacrumbs.shedlock</groupId>
    <artifactId>shedlock-provider-jdbc-template</artifactId>
    <version>2.1.0</version>
</dependency>
<dependency>
     <groupId>com.h2database</groupId>
     <artifactId>h2</artifactId>
     <version>1.4.200</version>
</dependency>

Next, we need to create a database table for ShedLock to keep information about scheduler locks:

CREATE TABLE shedlock (
  name VARCHAR(64),
  lock_until TIMESTAMP(3) NULL,
  locked_at TIMESTAMP(3) NULL,
  locked_by VARCHAR(255),
  PRIMARY KEY (name)
)

We should declare the data source in our Spring Boot application's properties file so that the DataSource bean can be Autowired.

Here we use the application.yml to define the data source of the H2 database:

spring:
  datasource:
    driverClassName: org.h2.Driver
    url: jdbc:h2:mem:shedlock_DB;INIT=CREATE SCHEMA IF NOT EXISTS shedlock;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE
    username: sa
    password:

Let's config the LockProvider with the data source configuration above.

Spring can make it pretty straightforward:

@Configuration
public class SchedulerConfiguration {
    @Bean
    public LockProvider lockProvider(DataSource dataSource) {
        return new JdbcTemplateLockProvider(dataSource);
    }
}

Other configuration requirements we have to provide are the @EnableScheduling and @EnableSchedulerLock annotations on our Spring configuration class:

@SpringBootApplication
@EnableScheduling
@EnableSchedulerLock(defaultLockAtMostFor = "PT30S")
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(SpringApplication.class, args);
    }
}

The defaultLockAtMostFor parameter specifies the default amount of time the lock should be kept in case the executing node dies. It uses the ISO8601 Duration format.

In the next section, we'll see how to override this default.

4. Creating Tasks

To create a scheduled task handled by ShedLock, we simply put the @Scheduled and @SchedulerLock annotations on a method:

@Component
class BaeldungTaskScheduler {

    @Scheduled(cron = "0 0/15 * * * ?")
    @SchedulerLock(name = "TaskScheduler_scheduledTask", 
      lockAtLeastForString = "PT5M", lockAtMostForString = "PT14M")
    public void scheduledTask() {
        // ...
    }
}

First, let's look at @Scheduled. It supports the cron format, with this expression meaning “every 15 minutes.”

Next, taking a look at @SchedulerLock, the name parameter has to be unique, and ClassName_methodName is typically enough to achieve that. We don't want more than one run of this method happening at the same time, and ShedLock uses the unique name to achieve that.

We've also added a couple of optional parameters.

First, we added lockAtLeastForString so that we can put some distance between method invocations. Using “PT5M” means that this method will hold the lock for five minutes, at a minimum. In other words, that means that this method can be run by ShedLock no more often than every five minutes.

Next, we added lockAtMostForString to specify how long the lock should be kept in case the executing node dies. Using “PT14M” means that it will be locked for no longer than 14 minutes.

In normal situations, ShedLock releases the lock directly after the task finishes. Now, we didn't have to do that because there is a default provided in @EnableSchedulerLock, but we've chosen to override that here.

5. Conclusion

In this article, we learned how to create and synchronize scheduled tasks using ShedLock.

As always, all source code is available over on GitHub.

Spring bottom

Get started with Spring 5 and Spring Boot 2, through the Learn Spring course:

>> THE COURSE
Generic footer banner
4 Comments
Oldest
Newest
Inline Feedbacks
View all comments
Comments are closed on this article!