DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Because the DevOps movement has redefined engineering responsibilities, SREs now have to become stewards of observability strategy.

Apache Cassandra combines the benefits of major NoSQL databases to support data management needs not covered by traditional RDBMS vendors.

The software you build is only as secure as the code that powers it. Learn how malicious code creeps into your software supply chain.

Generative AI has transformed nearly every industry. How can you leverage GenAI to improve your productivity and efficiency?

Related

  • Spring Microservice Tip: Abstracting the Database Hostname With Environment Variable
  • Enterprise RIA With Spring 3, Flex 4 and GraniteDS
  • Spring Boot: How To Use Java Persistence Query Language (JPQL)
  • How To Build Web Service Using Spring Boot 2.x

Trending

  • The Evolution of Scalable and Resilient Container Infrastructure
  • Supervised Fine-Tuning (SFT) on VLMs: From Pre-trained Checkpoints To Tuned Models
  • Chat With Your Knowledge Base: A Hands-On Java and LangChain4j Guide
  • Traditional Testing and RAGAS: A Hybrid Strategy for Evaluating AI Chatbots
  1. DZone
  2. Data Engineering
  3. Databases
  4. Dynamic Schedulers and Custom Cross-Server Schedule Lock

Dynamic Schedulers and Custom Cross-Server Schedule Lock

In this article, we gonna look into TaskScheduler and configure schedulers dynamically based on the database values.

By 
Venkatesh Rajendran user avatar
Venkatesh Rajendran
DZone Core CORE ·
Oct. 01, 20 · Analysis
Likes (3)
Comment
Save
Tweet
Share
5.2K Views

Join the DZone community and get the full member experience.

Join For Free

As part of java development, we always may run into a situation to configure a Job or Scheduler.
Quartz is one of the popular scheduling API.

In this article, we gonna look into TaskScheduler and configure schedulers dynamically based on the database values. This also includes simple mutex kind of logic, how can we avoid our scheduler running at a time when the app running as a cluster.

Before going into the topic, Let me give self inro. This is Venkatesh Rajendran, a full-stack software developer, and my key areas are Java, Spring framework (spring boot ), and hibernate, etc.. In the frontend , I'm not a big guy. I still learning. I have worked on jQuery and reactjs. And I'm exploring NodeJs as well. And This is my very first blog in my life :).

To manage and run our schedulers we need an ExecutorService. ThreadPoolTaskScheduler is one of the widely used SchedulingTaskExecutor. Let's create a bean of TaskScheduler using ThreadPoolTaskScheduler.

Java
 




xxxxxxxxxx
1


 
1
@Bean
2
public TaskScheduler taskScheduler(){
3
   ThreadPoolTaskScheduler threadPoolTaskScheduler = new ThreadPoolTaskScheduler();
4
   threadPoolTaskScheduler.setPoolSize(5);
5
   threadPoolTaskScheduler.setThreadNamePrefix("DynamicSchedules-");
6
   threadPoolTaskScheduler.initialize();
7
   return threadPoolTaskScheduler;
8
}



Let's move to the configurations part.

Since Schedulers are dynamically configured at run-time, We need to move our cron expressions and bean names that gonna run the job.

For these purposes, I have created an Entity called SchedulerConfigEntity.

Java
 




xxxxxxxxxx
1
16


 
1
@Data
2
@Entity
3
@Table(name = "scheduler_config")
4
public class SchedulerConfigEntity {
5

          
6
    @Id
7
    private Integer id;
8

          
9
    @Column(name = "bean_name")
10
    private String beanName;
11

          
12
    private String cron;
13

          
14
    @Enumerated(EnumType.STRING)
15
    private Lock lock_;
16
}



In general, A scheduler should run a block of code. That's the purpose of the scheduler. So It's better to have an Interface that abstracts this operation. Also, this interface includes two more abstract methods.

Java
 




xxxxxxxxxx
1


 
1
public interface Scheduler {
2
    void schedule(Integer id);
3
    boolean acquireLock(Integer id);
4
    void resetLock(Integer id);
5
}



acquireLock and resetLock are used for Cross-Server Scheduler Locking.

Let's create an implementation for this interface.

Java
 




xxxxxxxxxx
1
31


 
1
@Slf4j
2
@Component
3
public class ExampleSchedulerImpl  implements Scheduler {
4

          
5
    @Autowired
6
    private SchedulerConfigRepo schedulerConfigRepo;
7

          
8
    @Override
9
    @Transactional
10
    public void schedule(Integer id) {
11
        if(acquireLock(id)){
12
            log.warn("Running on another instance.");
13
            return;
14
        }
15

          
16
        log.info("Hey.. This is printed by dynamically scheduled tasks");
17
        resetLock(id);
18
    }
19

          
20
    @Override
21
    public boolean acquireLock(Integer id) {
22
        int rowsUpdated = schedulerConfigRepo.acquireLock(id);
23
        return rowsUpdated > 0;
24
    }
25

          
26
    @Override
27
    public void resetLock(Integer id) {
28
        schedulerConfigRepo.resetLock(id);
29
    }
30

          
31
}



Let's configure our scheduler,

Create an entry for SchedulerConfigEntity in db. Here I have a data.sql file that does the insert for us.

INSERT INTO scheduler_config VALUES

(1,'exampleSchedulerImpl','* * * * * *','OPEN');

And here our configuration class:

Java
 




xxxxxxxxxx
1
40


 
1
@Configuration
2
public class SchedulerConfig {
3

          
4
    // Todo future usage
5
    private static Map<String, ScheduledFuture<?>> futureMap = new HashMap<>();
6

          
7
    private SchedulerConfigRepo schedulerConfigRepo;
8
    private ApplicationContext applicationContext;
9
    private TaskScheduler taskScheduler;
10

          
11
    @Autowired
12
    public SchedulerConfig(SchedulerConfigRepo schedulerConfigRepo, ApplicationContext applicationContext, TaskScheduler taskScheduler) {
13
        this.schedulerConfigRepo = schedulerConfigRepo;
14
        this.applicationContext = applicationContext;
15
        this.taskScheduler = taskScheduler;
16
    }
17

          
18
    @PostConstruct
19
    public void configSchedules(){
20
        List<SchedulerConfigEntity> configs = schedulerConfigRepo.findAll();
21

          
22
        for (SchedulerConfigEntity config : configs) {
23
            // Get the ExampleSchedulerImpl that implements Scheduler, default bean name for ExampleSchedulerImpl will be exampleSchedulerImpl.
24
            Scheduler scheduler = (Scheduler) applicationContext.getBean(config.getBeanName());
25

          
26
            if(Objects.isNull(futureMap.get(config.getBeanName()))) {
27

          
28
                // Schedule the task with cron expression
29
                ScheduledFuture<?> schedule = taskScheduler.schedule(()->{
30
                    scheduler.schedule(config.getId());
31
                }, new CronTrigger(config.getCron()));
32

          
33
                //ScheduledFuture can be used for checking the job status.
34
                futureMap.put(config.getBeanName(), schedule);
35
            }
36
        }
37
    }
38

          
39
}
40

          



How do we enable Cross Server Scheduler lock, here we have introduced a column called lock_ with default='OPEN'. When starting the job It will try to acquire the lock and set to 'LOCKED' If not It should be running on another instance. When the job ends or fails, we should reset the lock back to 'OPEN'. Let's check the JPA repository how it sets and resets the lock.

Java
 




xxxxxxxxxx
1
11


 
1
public interface SchedulerConfigRepo extends JpaRepository<SchedulerConfigEntity, Integer> {
2

          
3
    @Modifying
4
    @Query(value = "UPDATE scheduler_config SET lock_='LOCKED' WHERE id=:id AND lock_='OPEN'", nativeQuery = true)
5
    int acquireLock(@Param("id") Integer id);
6

          
7
    @Modifying
8
    @Query(value = "UPDATE scheduler_config SET lock_='OPEN' WHERE id=:id", nativeQuery = true)
9
    void resetLock(@Param("id") Integer id);
10

          
11
}



That's all. So we have an implementation for DynamicScheduler that reads the configuration from the database and a custom cross-server scheduler locking strategy. Please find the full implementation in my Git Repo.

job scheduling Lock (computer science) Database Spring Framework Java (programming language) Schedule (computer science)

Opinions expressed by DZone contributors are their own.

Related

  • Spring Microservice Tip: Abstracting the Database Hostname With Environment Variable
  • Enterprise RIA With Spring 3, Flex 4 and GraniteDS
  • Spring Boot: How To Use Java Persistence Query Language (JPQL)
  • How To Build Web Service Using Spring Boot 2.x

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!