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:
Tadgh 2024-06-15 18:15:55 -07:00 committed by GitHub
parent 73a6a8ef82
commit 87fab18123
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 55 additions and 9 deletions

View File

@ -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."

View File

@ -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;
}
} }

View File

@ -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);
}
} }