Shedlock and Micronaut, a super easy distributed lock solution
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!