Skip to content

Commit

Permalink
Initial implementation with properties configured schedulers
Browse files Browse the repository at this point in the history
  • Loading branch information
maallen committed Aug 23, 2023
1 parent 73d3a22 commit e4be83e
Show file tree
Hide file tree
Showing 16 changed files with 392 additions and 149 deletions.
92 changes: 64 additions & 28 deletions docker/docker-compose-api-worker.yml
Original file line number Diff line number Diff line change
Expand Up @@ -59,20 +59,38 @@ services:
"spring.datasource.driverClassName" : "com.mysql.cj.jdbc.Driver",
"spring.jpa.defer-datasource-initialization" : "false",
"l10n.org.quartz.scheduler.enabled" : "true",
"l10n.org.quartz.jobStore.useProperties" : "true",
"l10n.org.quartz.scheduler.instanceId" : "AUTO",
"l10n.org.quartz.jobStore.isClustered" : "true",
"l10n.org.quartz.threadPool.threadCount" : "10",
"l10n.org.quartz.jobStore.class" : "org.quartz.impl.jdbcjobstore.JobStoreTX",
"l10n.org.quartz.jobStore.driverDelegateClass" : "org.quartz.impl.jdbcjobstore.StdJDBCDelegate",
"l10n.org.quartz.jobStore.dataSource" : "myDS",
"l10n.org.quartz.dataSource.myDS.provider" : "hikaricp",
"l10n.org.quartz.dataSource.myDS.driver" : "com.mysql.jdbc.Driver",
"l10n.org.quartz.dataSource.myDS.URL" : "jdbc:mysql://db:3306/mojito?characterEncoding=UTF-8&useUnicode=true",
"l10n.org.quartz.dataSource.myDS.user" : "mojito",
"l10n.org.quartz.dataSource.myDS.password" : "ChangeMe",
"l10n.org.quartz.dataSource.myDS.maxConnections" : "12",
"l10n.org.quartz.dataSource.myDS.validationQuery" : "select 1"
"l10n.org.multi-quartz.enabled" : "true",
"l10n.org.multi-quartz.schedulerConfigs[0].name" : "defaultScheduler",
"l10n.org.multi-quartz.schedulerConfigs[0].quartz.jobStore.useProperties" : "true",
"l10n.org.multi-quartz.schedulerConfigs[0].quartz.scheduler.instanceId" : "AUTO",
"l10n.org.multi-quartz.schedulerConfigs[0].quartz.jobStore.isClustered" : "true",
"l10n.org.multi-quartz.schedulerConfigs[0].quartz.threadPool.threadCount" : 10,
"l10n.org.multi-quartz.schedulerConfigs[0].quartz.jobStore.class" : "org.quartz.impl.jdbcjobstore.JobStoreTX",
"l10n.org.multi-quartz.schedulerConfigs[0].quartz.jobStore.driverDelegateClass" : "org.quartz.impl.jdbcjobstore.StdJDBCDelegate",
"l10n.org.multi-quartz.schedulerConfigs[0].quartz.jobStore.dataSource" : "myDS",
"l10n.org.multi-quartz.schedulerConfigs[0].quartz.dataSource.myDS.provider" : "hikaricp",
"l10n.org.multi-quartz.schedulerConfigs[0].quartz.dataSource.myDS.driver" : "com.mysql.jdbc.Driver",
"l10n.org.multi-quartz.schedulerConfigs[0].quartz.dataSource.myDS.URL" : "jdbc:mysql://db:3306/mojito?characterEncoding=UTF-8&useUnicode=true",
"l10n.org.multi-quartz.schedulerConfigs[0].quartz.dataSource.myDS.user" : "mojito",
"l10n.org.multi-quartz.schedulerConfigs[0].quartz.dataSource.myDS.password" : "ChangeMe",
"l10n.org.multi-quartz.schedulerConfigs[0].quartz.dataSource.myDS.maxConnections" : 12,
"l10n.org.multi-quartz.schedulerConfigs[0].quartz.dataSource.myDS.validationQuery" : "select 1",
"l10n.org.multi-quartz.schedulerConfigs[1].name" : "lowPriority",
"l10n.org.multi-quartz.schedulerConfigs[1].quartz.jobStore.useProperties" : "true",
"l10n.org.multi-quartz.schedulerConfigs[1].quartz.scheduler.instanceId" : "AUTO",
"l10n.org.multi-quartz.schedulerConfigs[1].quartz.jobStore.isClustered" : "true",
"l10n.org.multi-quartz.schedulerConfigs[1].quartz.threadPool.threadCount" : 5,
"l10n.org.multi-quartz.schedulerConfigs[1].quartz.jobStore.class" : "org.quartz.impl.jdbcjobstore.JobStoreTX",
"l10n.org.multi-quartz.schedulerConfigs[1].quartz.jobStore.driverDelegateClass" : "org.quartz.impl.jdbcjobstore.StdJDBCDelegate",
"l10n.org.multi-quartz.schedulerConfigs[1].quartz.jobStore.dataSource" : "myDS",
"l10n.org.multi-quartz.schedulerConfigs[1].quartz.dataSource.myDS.provider" : "hikaricp",
"l10n.org.multi-quartz.schedulerConfigs[1].quartz.dataSource.myDS.driver" : "com.mysql.jdbc.Driver",
"l10n.org.multi-quartz.schedulerConfigs[1].quartz.dataSource.myDS.URL" : "jdbc:mysql://db:3306/mojito?characterEncoding=UTF-8&useUnicode=true",
"l10n.org.multi-quartz.schedulerConfigs[1].quartz.dataSource.myDS.user" : "mojito",
"l10n.org.multi-quartz.schedulerConfigs[1].quartz.dataSource.myDS.password" : "ChangeMe",
"l10n.org.multi-quartz.schedulerConfigs[1].quartz.dataSource.myDS.maxConnections" : 12,
"l10n.org.multi-quartz.schedulerConfigs[1].quartz.dataSource.myDS.validationQuery" : "select 1",
"l10n.assetExtraction.quartz.schedulerName" : "lowPriority"
}'
api:
deploy:
Expand Down Expand Up @@ -114,18 +132,36 @@ services:
"spring.datasource.driverClassName" : "com.mysql.cj.jdbc.Driver",
"spring.jpa.defer-datasource-initialization" : "false",
"l10n.org.quartz.scheduler.enabled" : "false",
"l10n.org.quartz.jobStore.useProperties" : "true",
"l10n.org.quartz.scheduler.instanceId" : "AUTO",
"l10n.org.quartz.jobStore.isClustered" : "true",
"l10n.org.quartz.threadPool.threadCount" : "10",
"l10n.org.quartz.jobStore.class" : "org.quartz.impl.jdbcjobstore.JobStoreTX",
"l10n.org.quartz.jobStore.driverDelegateClass" : "org.quartz.impl.jdbcjobstore.StdJDBCDelegate",
"l10n.org.quartz.jobStore.dataSource" : "myDS",
"l10n.org.quartz.dataSource.myDS.provider" : "hikaricp",
"l10n.org.quartz.dataSource.myDS.driver" : "com.mysql.jdbc.Driver",
"l10n.org.quartz.dataSource.myDS.URL" : "jdbc:mysql://db:3306/mojito?characterEncoding=UTF-8&useUnicode=true",
"l10n.org.quartz.dataSource.myDS.user" : "mojito",
"l10n.org.quartz.dataSource.myDS.password" : "ChangeMe",
"l10n.org.quartz.dataSource.myDS.maxConnections" : "12",
"l10n.org.quartz.dataSource.myDS.validationQuery" : "select 1"
"l10n.org.multi-quartz.enabled" : "true",
"l10n.org.multi-quartz.schedulerConfigs[0].name" : "defaultScheduler",
"l10n.org.multi-quartz.schedulerConfigs[0].quartz.jobStore.useProperties" : "true",
"l10n.org.multi-quartz.schedulerConfigs[0].quartz.scheduler.instanceId" : "AUTO",
"l10n.org.multi-quartz.schedulerConfigs[0].quartz.jobStore.isClustered" : "true",
"l10n.org.multi-quartz.schedulerConfigs[0].quartz.threadPool.threadCount" : 10,
"l10n.org.multi-quartz.schedulerConfigs[0].quartz.jobStore.class" : "org.quartz.impl.jdbcjobstore.JobStoreTX",
"l10n.org.multi-quartz.schedulerConfigs[0].quartz.jobStore.driverDelegateClass" : "org.quartz.impl.jdbcjobstore.StdJDBCDelegate",
"l10n.org.multi-quartz.schedulerConfigs[0].quartz.jobStore.dataSource" : "myDS",
"l10n.org.multi-quartz.schedulerConfigs[0].quartz.dataSource.myDS.provider" : "hikaricp",
"l10n.org.multi-quartz.schedulerConfigs[0].quartz.dataSource.myDS.driver" : "com.mysql.jdbc.Driver",
"l10n.org.multi-quartz.schedulerConfigs[0].quartz.dataSource.myDS.URL" : "jdbc:mysql://db:3306/mojito?characterEncoding=UTF-8&useUnicode=true",
"l10n.org.multi-quartz.schedulerConfigs[0].quartz.dataSource.myDS.user" : "mojito",
"l10n.org.multi-quartz.schedulerConfigs[0].quartz.dataSource.myDS.password" : "ChangeMe",
"l10n.org.multi-quartz.schedulerConfigs[0].quartz.dataSource.myDS.maxConnections" : 12,
"l10n.org.multi-quartz.schedulerConfigs[0].quartz.dataSource.myDS.validationQuery" : "select 1",
"l10n.org.multi-quartz.schedulerConfigs[1].name" : "lowPriority",
"l10n.org.multi-quartz.schedulerConfigs[1].quartz.jobStore.useProperties" : "true",
"l10n.org.multi-quartz.schedulerConfigs[1].quartz.scheduler.instanceId" : "AUTO",
"l10n.org.multi-quartz.schedulerConfigs[1].quartz.jobStore.isClustered" : "true",
"l10n.org.multi-quartz.schedulerConfigs[1].quartz.threadPool.threadCount" : 5,
"l10n.org.multi-quartz.schedulerConfigs[1].quartz.jobStore.class" : "org.quartz.impl.jdbcjobstore.JobStoreTX",
"l10n.org.multi-quartz.schedulerConfigs[1].quartz.jobStore.driverDelegateClass" : "org.quartz.impl.jdbcjobstore.StdJDBCDelegate",
"l10n.org.multi-quartz.schedulerConfigs[1].quartz.jobStore.dataSource" : "myDS",
"l10n.org.multi-quartz.schedulerConfigs[1].quartz.dataSource.myDS.provider" : "hikaricp",
"l10n.org.multi-quartz.schedulerConfigs[1].quartz.dataSource.myDS.driver" : "com.mysql.jdbc.Driver",
"l10n.org.multi-quartz.schedulerConfigs[1].quartz.dataSource.myDS.URL" : "jdbc:mysql://db:3306/mojito?characterEncoding=UTF-8&useUnicode=true",
"l10n.org.multi-quartz.schedulerConfigs[1].quartz.dataSource.myDS.user" : "mojito",
"l10n.org.multi-quartz.schedulerConfigs[1].quartz.dataSource.myDS.password" : "ChangeMe",
"l10n.org.multi-quartz.schedulerConfigs[1].quartz.dataSource.myDS.maxConnections" : 12,
"l10n.org.multi-quartz.schedulerConfigs[1].quartz.dataSource.myDS.validationQuery" : "select 1",
"l10n.assetExtraction.quartz.schedulerName" : "lowPriority"
}'
27 changes: 17 additions & 10 deletions webapp/src/main/java/com/box/l10n/mojito/quartz/QuartzConfig.java
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
package com.box.l10n.mojito.quartz;

import static com.box.l10n.mojito.quartz.QuartzSchedulerManager.DEFAULT_SCHEDULER_NAME;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Properties;
import java.util.Set;
import javax.annotation.PostConstruct;
import org.quartz.JobDetail;
Expand All @@ -16,6 +17,7 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component
Expand All @@ -25,15 +27,16 @@ public class QuartzConfig {

public static final String DYNAMIC_GROUP_NAME = "DYNAMIC";

@Autowired List<Scheduler> schedulers;
@Autowired QuartzSchedulerManager schedulerManager;

@Autowired(required = false)
List<Trigger> triggers = new ArrayList<>();

@Autowired(required = false)
List<JobDetail> jobDetails = new ArrayList<>();

@Autowired QuartzPropertiesConfig quartzPropertiesConfig;
@Value("${l10n.org.quartz.scheduler.enabled:true}")
Boolean schedulerEnabled;

/**
* Starts the scheduler after having removed outdated trigger/jobs
Expand All @@ -42,24 +45,28 @@ public class QuartzConfig {
*/
@PostConstruct
void startSchedulers() throws SchedulerException {
Properties quartzProps = quartzPropertiesConfig.getQuartzProperties();
removeOutdatedJobs();
if (Boolean.parseBoolean(quartzProps.getProperty("org.quartz.scheduler.enabled", "true"))) {
logger.info("Starting schedulers");
for (Scheduler scheduler : schedulers) {
scheduler.startDelayed(2);
int delay = 2;
if (schedulerEnabled) {
for (Scheduler scheduler : schedulerManager.getSchedulers()) {
logger.info("Starting scheduler: {}", scheduler.getSchedulerName());
scheduler.startDelayed(delay);
delay++;
}
}
}

void removeOutdatedJobs() throws SchedulerException {
for (Scheduler scheduler : schedulers) {
scheduler.unscheduleJobs(new ArrayList<TriggerKey>(getOutdatedTriggerKeys(scheduler)));
for (Scheduler scheduler : schedulerManager.getSchedulers()) {
if (scheduler.getSchedulerName().equals(DEFAULT_SCHEDULER_NAME)) {
scheduler.unscheduleJobs(new ArrayList<TriggerKey>(getOutdatedTriggerKeys(scheduler)));
}
scheduler.deleteJobs(new ArrayList<JobKey>(getOutdatedJobKeys(scheduler)));
}
}

Set<JobKey> getOutdatedJobKeys(Scheduler scheduler) throws SchedulerException {

Set<JobKey> jobKeys =
scheduler.getJobKeys(GroupMatcher.jobGroupEquals(Scheduler.DEFAULT_GROUP));
Set<JobKey> newJobKeys = new HashSet<>();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.box.l10n.mojito.quartz;

import static com.box.l10n.mojito.quartz.QuartzConfig.DYNAMIC_GROUP_NAME;
import static com.box.l10n.mojito.quartz.QuartzQueue.DEFAULT;
import static com.box.l10n.mojito.quartz.QuartzSchedulerManager.DEFAULT_SCHEDULER_NAME;

import com.box.l10n.mojito.entity.PollableTask;
import com.box.l10n.mojito.json.ObjectMapper;
Expand Down Expand Up @@ -42,7 +42,7 @@ public <I, O> PollableFuture<O> scheduleJob(
Class<? extends QuartzPollableJob<I, O>> clazz, I input) {
QuartzJobInfo<I, O> quartzJobInfo =
QuartzJobInfo.newBuilder(clazz).withInput(input).withMessage(clazz.getSimpleName()).build();
return scheduleJob(quartzJobInfo, DEFAULT);
return scheduleJob(quartzJobInfo, DEFAULT_SCHEDULER_NAME);
}

public <I, O> PollableFuture<O> scheduleJobWithCustomTimeout(
Expand All @@ -54,11 +54,11 @@ public <I, O> PollableFuture<O> scheduleJobWithCustomTimeout(
.withMessage(clazz.getSimpleName())
.build();

return scheduleJob(quartzJobInfo, DEFAULT);
return scheduleJob(quartzJobInfo, DEFAULT_SCHEDULER_NAME);
}

public <I, O> PollableFuture<O> scheduleJob(QuartzJobInfo<I, O> quartzJobInfo) {
return scheduleJob(quartzJobInfo, DEFAULT);
return scheduleJob(quartzJobInfo, DEFAULT_SCHEDULER_NAME);
}

/**
Expand All @@ -80,11 +80,11 @@ public <I, O> PollableFuture<O> scheduleJob(QuartzJobInfo<I, O> quartzJobInfo) {
* @return
*/
public <I, O> PollableFuture<O> scheduleJob(
QuartzJobInfo<I, O> quartzJobInfo, QuartzQueue quartzQueue) {
QuartzJobInfo<I, O> quartzJobInfo, String schedulerName) {

Scheduler scheduler = schedulerManager.getScheduler(quartzQueue);
Scheduler scheduler = schedulerManager.getScheduler(schedulerName);

logger.debug("Scheduling job on queue: {}", quartzQueue.getDescription());
logger.debug("Scheduling job on queue: {}", schedulerName);

String pollableTaskName = getPollableTaskName(quartzJobInfo.getClazz());

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,17 @@
import java.util.Properties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
@ConfigurationProperties("l10n.org")
@ConditionalOnProperty(
name = "l10n.org.multi-quartz.enabled",
havingValue = "false",
matchIfMissing = true)
public class QuartzPropertiesConfig {

static Logger logger = LoggerFactory.getLogger(QuartzPropertiesConfig.class);
Expand All @@ -31,6 +36,9 @@ public Properties getQuartzProperties() {
logger.debug("org.quartz.{}={}", entry.getKey(), entry.getValue());
}

properties.put(
"org.quartz.scheduler.instanceName", QuartzSchedulerManager.DEFAULT_SCHEDULER_NAME);

return properties;
}
}
17 changes: 0 additions & 17 deletions webapp/src/main/java/com/box/l10n/mojito/quartz/QuartzQueue.java

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.box.l10n.mojito.quartz;

public class QuartzQueueException extends RuntimeException {

public QuartzQueueException(String message) {
super(message);
}

public QuartzQueueException(String message, Throwable cause) {
super(message, cause);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@
import java.util.List;
import java.util.Properties;
import javax.sql.DataSource;
import org.quartz.SchedulerException;
import org.quartz.Trigger;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
Expand All @@ -18,6 +19,10 @@
import org.springframework.transaction.PlatformTransactionManager;

@Configuration
@ConditionalOnProperty(
name = "l10n.org.multi-quartz.enabled",
havingValue = "false",
matchIfMissing = true)
public class QuartzSchedulerConfig {

/** logger */
Expand Down Expand Up @@ -50,52 +55,24 @@ public class QuartzSchedulerConfig {
* removed.
*
* @return
* @throws SchedulerException
*/
@Bean
public SchedulerFactoryBean scheduler() throws SchedulerException {

// TODO (maallen): Update config to read multiple schedulers config from app.properties so each
// scheduler can have it's own configured thread pool.
@Bean(name = "defaultScheduler")
public SchedulerFactoryBean defaultScheduler(
@Qualifier("defaultJobFactory") SpringBeanJobFactory jobFactory) {
logger.info("Create SchedulerFactoryBean");

logger.info("Create default Scheduler");

Properties quartzProperties = quartzPropertiesConfig.getQuartzProperties();
quartzProperties.put("org.quartz.scheduler.instanceName", "defaultScheduler");

SchedulerFactoryBean factory = getSchedulerFactory(quartzProperties, jobFactory);
factory.setTriggers(triggers.toArray(new Trigger[] {}));
return factory;
}

@Bean(name = "lowPriorityScheduler")
public SchedulerFactoryBean lowPriorityScheduler(
@Qualifier("lowPriorityJobFactory") SpringBeanJobFactory jobFactory) {

logger.info("Create Low Priority Scheduler");

Properties quartzProperties = quartzPropertiesConfig.getQuartzProperties();
quartzProperties.put("org.quartz.scheduler.instanceName", "lowPriorityScheduler");

return getSchedulerFactory(quartzProperties, jobFactory);
}

@Bean(name = "highPriorityScheduler")
public SchedulerFactoryBean highPriorityScheduler(
@Qualifier("highPriorityJobFactory") SpringBeanJobFactory jobFactory) {
logger.info("Create High Priority Scheduler");
Properties quartzProperties = quartzPropertiesConfig.getQuartzProperties();
quartzProperties.put("org.quartz.scheduler.instanceName", "highPriorityScheduler");
return getSchedulerFactory(quartzProperties, jobFactory);
}

private SchedulerFactoryBean getSchedulerFactory(
Properties quartzProperties, SpringBeanJobFactory springBeanJobFactory) {
SchedulerFactoryBean schedulerFactory = new SchedulerFactoryBean();

String dataSource = quartzProperties.getProperty("org.quartz.jobStore.dataSource");
schedulerFactory.setQuartzProperties(quartzProperties);
schedulerFactory.setJobFactory(springBeanJobFactory);
schedulerFactory.setJobFactory(springBeanJobFactory());
schedulerFactory.setOverwriteExistingJobs(true);
schedulerFactory.setTriggers(triggers.toArray(new Trigger[] {}));
schedulerFactory.setAutoStartup(false);
schedulerFactory.setBeanName(QuartzSchedulerManager.DEFAULT_SCHEDULER_NAME);

if (quartzMetricsReportingJobListener != null) {
schedulerFactory.setGlobalJobListeners(quartzMetricsReportingJobListener);
Expand All @@ -104,22 +81,8 @@ private SchedulerFactoryBean getSchedulerFactory(
return schedulerFactory;
}

@Bean(name = "lowPriorityJobFactory")
public SpringBeanJobFactory lowPrioritySpringBeanJobFactory() {
AutoWiringSpringBeanJobFactory jobFactory = new AutoWiringSpringBeanJobFactory();
jobFactory.setApplicationContext(applicationContext);
return jobFactory;
}

@Bean(name = "defaultJobFactory")
public SpringBeanJobFactory defaultSpringBeanJobFactory() {
AutoWiringSpringBeanJobFactory jobFactory = new AutoWiringSpringBeanJobFactory();
jobFactory.setApplicationContext(applicationContext);
return jobFactory;
}

@Bean(name = "highPriorityJobFactory")
public SpringBeanJobFactory highPrioritySpringBeanJobFactory() {
@Bean
public SpringBeanJobFactory springBeanJobFactory() {
AutoWiringSpringBeanJobFactory jobFactory = new AutoWiringSpringBeanJobFactory();
jobFactory.setApplicationContext(applicationContext);
return jobFactory;
Expand Down
Loading

0 comments on commit e4be83e

Please sign in to comment.