6007 scheduler id problems with multiple instances (#6012)
* Modify cluster instance name * spotless * Add test for scheduler name * Changelog * Remove atomic int for autogenerating IDs * Remove atomic int for autogenerating IDs * Fix test
This commit is contained in:
parent
73a6a8ef82
commit
87fab18123
|
@ -0,0 +1,6 @@
|
||||||
|
---
|
||||||
|
type: fix
|
||||||
|
issue: 6007
|
||||||
|
title: "Previously, HAPI-FHIR schedulers were automatically suffixing the Quartz scheduler's `org.quartz.scheduler.instanceName` value to a unique autogenerated value based on the provided instance name. This
|
||||||
|
has been corrected, and now the instance name provided to the BaseHapiFhirScheduler is used directly, with no unique suffix added. This is in line with the [Quartz Documentation](https://www.quartz-scheduler.org/documentation/quartz-2.1.7/configuration/ConfigMain.html) related
|
||||||
|
to defining the scheduler instance name for those who run schedulers in a cluster."
|
|
@ -48,7 +48,6 @@ import org.springframework.scheduling.quartz.SchedulerFactoryBean;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||||
|
@ -56,8 +55,6 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||||
public abstract class BaseHapiScheduler implements IHapiScheduler {
|
public abstract class BaseHapiScheduler implements IHapiScheduler {
|
||||||
private static final Logger ourLog = LoggerFactory.getLogger(BaseHapiScheduler.class);
|
private static final Logger ourLog = LoggerFactory.getLogger(BaseHapiScheduler.class);
|
||||||
|
|
||||||
private static final AtomicInteger ourNextSchedulerId = new AtomicInteger();
|
|
||||||
|
|
||||||
private final String myThreadNamePrefix;
|
private final String myThreadNamePrefix;
|
||||||
private final AutowiringSpringBeanJobFactory mySpringBeanJobFactory;
|
private final AutowiringSpringBeanJobFactory mySpringBeanJobFactory;
|
||||||
private final SchedulerFactoryBean myFactory = new SchedulerFactoryBean();
|
private final SchedulerFactoryBean myFactory = new SchedulerFactoryBean();
|
||||||
|
@ -75,18 +72,16 @@ public abstract class BaseHapiScheduler implements IHapiScheduler {
|
||||||
myInstanceName = theInstanceName;
|
myInstanceName = theInstanceName;
|
||||||
}
|
}
|
||||||
|
|
||||||
int nextSchedulerId() {
|
|
||||||
return ourNextSchedulerId.getAndIncrement();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void init() throws SchedulerException {
|
public void init() throws SchedulerException {
|
||||||
|
|
||||||
setProperties();
|
setProperties();
|
||||||
myFactory.setQuartzProperties(myProperties);
|
myFactory.setQuartzProperties(myProperties);
|
||||||
myFactory.setBeanName(myInstanceName);
|
myFactory.setBeanName(myInstanceName);
|
||||||
myFactory.setSchedulerName(myThreadNamePrefix);
|
myFactory.setSchedulerName(myThreadNamePrefix);
|
||||||
myFactory.setJobFactory(mySpringBeanJobFactory);
|
myFactory.setJobFactory(mySpringBeanJobFactory);
|
||||||
massageJobFactory(myFactory);
|
massageJobFactory(myFactory);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Validate.notBlank(myInstanceName, "No instance name supplied");
|
Validate.notBlank(myInstanceName, "No instance name supplied");
|
||||||
myFactory.afterPropertiesSet();
|
myFactory.afterPropertiesSet();
|
||||||
|
@ -104,8 +99,17 @@ public abstract class BaseHapiScheduler implements IHapiScheduler {
|
||||||
|
|
||||||
protected void setProperties() {
|
protected void setProperties() {
|
||||||
addProperty("org.quartz.threadPool.threadCount", "4");
|
addProperty("org.quartz.threadPool.threadCount", "4");
|
||||||
myProperties.setProperty(
|
// Note that we use a common name, with no suffixed ID for the name, as per the quartz docs:
|
||||||
StdSchedulerFactory.PROP_SCHED_INSTANCE_NAME, myInstanceName + "-" + nextSchedulerId());
|
// https://www.quartz-scheduler.org/documentation/quartz-2.1.7/configuration/ConfigMain.html
|
||||||
|
if (myInstanceName != null) {
|
||||||
|
myProperties.setProperty(StdSchedulerFactory.PROP_SCHED_INSTANCE_NAME, myInstanceName);
|
||||||
|
}
|
||||||
|
|
||||||
|
// By Default, the scheduler ID is not set, which will cause quartz to set it to the string NON_CLUSTERED. Here
|
||||||
|
// we are setting it explicitly as an indication to implementers that if they want a different ID, they should
|
||||||
|
// set it using this below property.
|
||||||
|
addProperty(StdSchedulerFactory.PROP_SCHED_INSTANCE_ID, StdSchedulerFactory.DEFAULT_INSTANCE_ID);
|
||||||
|
|
||||||
addProperty("org.quartz.threadPool.threadNamePrefix", getThreadPrefix());
|
addProperty("org.quartz.threadPool.threadNamePrefix", getThreadPrefix());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -284,4 +288,14 @@ public abstract class BaseHapiScheduler implements IHapiScheduler {
|
||||||
ourLog.error("Error triggering scheduled job with key {}", theJobDefinition);
|
ourLog.error("Error triggering scheduled job with key {}", theJobDefinition);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves a clone of the properties required for unit testing.
|
||||||
|
*
|
||||||
|
* @return The properties for unit testing.
|
||||||
|
*/
|
||||||
|
@VisibleForTesting
|
||||||
|
protected Properties getPropertiesForUnitTest() {
|
||||||
|
return myProperties;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,9 @@ package ca.uhn.fhir.jpa.sched;
|
||||||
import ca.uhn.fhir.i18n.Msg;
|
import ca.uhn.fhir.i18n.Msg;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.quartz.SchedulerException;
|
import org.quartz.SchedulerException;
|
||||||
|
import org.quartz.impl.StdSchedulerFactory;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
import static org.junit.jupiter.api.Assertions.fail;
|
import static org.junit.jupiter.api.Assertions.fail;
|
||||||
|
|
||||||
|
@ -21,5 +23,29 @@ public class BaseHapiSchedulerTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSchedulersShareTheSameServiceName() throws SchedulerException {
|
||||||
|
|
||||||
|
String instanceName = "local-scheduler";
|
||||||
|
String instanceID = "NON_CLUSTERED";
|
||||||
|
|
||||||
|
BaseHapiScheduler firstScheduler = new BaseHapiScheduler("hello", new AutowiringSpringBeanJobFactory()) {
|
||||||
|
};
|
||||||
|
firstScheduler.setInstanceName(instanceName);
|
||||||
|
|
||||||
|
BaseHapiScheduler secondScheduler = new BaseHapiScheduler("hello", new AutowiringSpringBeanJobFactory()) {
|
||||||
|
};
|
||||||
|
secondScheduler.setInstanceName(instanceName);
|
||||||
|
|
||||||
|
firstScheduler.init();
|
||||||
|
secondScheduler.init();
|
||||||
|
|
||||||
|
assertThat(firstScheduler.getPropertiesForUnitTest()).containsEntry(StdSchedulerFactory.PROP_SCHED_INSTANCE_NAME, instanceName);
|
||||||
|
assertThat(secondScheduler.getPropertiesForUnitTest()).containsEntry(StdSchedulerFactory.PROP_SCHED_INSTANCE_NAME, instanceName);
|
||||||
|
|
||||||
|
assertThat(firstScheduler.getPropertiesForUnitTest()).containsEntry(StdSchedulerFactory.PROP_SCHED_INSTANCE_ID, instanceID);
|
||||||
|
assertThat(secondScheduler.getPropertiesForUnitTest()).containsEntry(StdSchedulerFactory.PROP_SCHED_INSTANCE_ID, instanceID);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue