Shedlock and Micronaut, a super easy distributed lock solution

Share on:

I guess I am old enough to remember the days when I had to configure Quartz on some old JavaEE application, troubleshoot a failed task maybe (Quartz is a very good library by the way)? Maybe some times it was a @Schedule EJB 3 job? Years go by, and we start getting used of Spring's @Scheduled beans, Micronaut has a similar annotation. Deploying our Spring Boot or Micronaut applications into platforms like Kubernetes, had a small side effect on these operations. Our scheduled jobs are _vm specific, so if we spin 3 replicas of our service that happens to feature at least one @Scheduled bean, we will unfortunately see 3 times the job executing, since the scheduler is bound to each specific instance. I guess you found your self in this situation several times, and maybe you did indeed resolve do to some custom distributed lock solution or used Quartz or something similar.

I was super happy to discover Shedlock, which is a super neat, very well documented, little library that works really well with SpringBoot and Micronaut and solves exactly that. It gives you with minimal side effects and changes a transparent distributed lock solution on top of your @Scheduled_ jobs. No need to change your code. The only dependency it brings in, is the use of some datasource, either it is a DB, a NoSQL datastore or a cache.

The main idea is that Shedlock will decorate your Scheduled methods, and based on the selected datasource (it needs a DB/etc) will implement a lock, where the different instances of your service will use. The end result is that only one out of X instances of your service will execute the job. If one fails then the job will continue executing on the other services. Assuming of course your job is _stateless'.

If you want to try it out, I have a very simple example using Micronaut and Redis. You can find the repo HERE.

If you clone the repo above, execute run.sh it will use docker-compose to spin 2 instances of a small micronaut microservice, that features one @Scheduled method that simply prints something on the logs. The method is decorated with 'Shedlock'. I have opted to use Redis as the datastore for Shedlock.

 1
 2@Singleton
 3@Slf4jpublic
 4class SampleJob {
 5    @Scheduled(fixedDelay = "20s", initialDelay = "5s")
 6    @SchedulerLock(name = "redislockjob", lockAtLeastFor = "5s")
 7    public void execute() {
 8        log.info("Job executed!");
 9    }
10}

In case you spin the docker-compose stack you will see something like the following. Redis is started, along with 2 identical instances of our service. The lock is being held my instance 1 which executes our job every 5 sec! Sweet!

Simple as that, I highly recommend you give it a try!