@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
@Override
public IWorker getWorkerIfItShouldRun(Integer serviceId, long nextTimeout) {
IWorker worker = null;
ServiceData serviceData = serviceDataSession.findById(serviceId);
ServiceConfiguration serviceConfiguration = serviceData.getServiceConfiguration();
if (!serviceConfiguration.isActive()) {
if (log.isDebugEnabled()) {
log.debug("Service " + serviceId + " is inactive.");
}
return null; // Don't return an inactive worker to run
}
String serviceName = serviceData.getName();
final String hostname = getHostName();
if (shouldRunOnThisNode(hostname, Arrays.asList(serviceConfiguration.getPinToNodes()))) {
long oldRunTimeStamp = serviceData.getRunTimeStamp();
long oldNextRunTimeStamp = serviceData.getNextRunTimeStamp();
worker = getWorker(serviceConfiguration, serviceName, oldRunTimeStamp, oldNextRunTimeStamp);
if (worker.getNextInterval() == IInterval.DONT_EXECUTE) {
if (log.isDebugEnabled()) {
log.debug("Service has interval IInterval.DONT_EXECUTE.");
}
return null; // Don't return an inactive worker to run
}
Date runDateCheck = new Date(oldNextRunTimeStamp); // nextRunDateCheck will typically be the same (or just a millisecond earlier) as now here
Date currentDate = new Date();
if (log.isDebugEnabled()) {
Date nextRunDate = new Date(nextTimeout);
log.debug("nextRunDate is: " + nextRunDate);
log.debug("runDateCheck is: " + runDateCheck);
log.debug("currentDate is: " + currentDate);
}
/*
* Check if the current date is after when the service should run. If a
* service on another cluster node has updated this timestamp already,
* then it will return false and this service will not run. This is a
* semaphore (not the best one admitted) so that services in a cluster
* only runs on one node and don't compete with each other. If a worker
* on one node for instance runs for a very long time, there is a chance
* that another worker on another node will break this semaphore and run
* as well.
*/
if (currentDate.after(runDateCheck)) {
/*
* We only update the nextRunTimeStamp if the service is allowed to run on this node.
*
* However, we need to make sure that no other node has already acquired the semaphore
* if our current database allows non-repeatable reads.
*/
if (!serviceDataSession.updateTimestamps(serviceId, oldRunTimeStamp, oldNextRunTimeStamp, runDateCheck.getTime(), nextTimeout)) {
log.debug("Another node had already updated the database at this point. This node will not run.");
worker = null; // Failed to update the database.
}
} else {
worker = null; // Don't return a worker, since this node should not run
}
} else {
worker = null;
if (log.isDebugEnabled()) {
log.debug("Service " + serviceName + " will not run on this node: \"" + hostname + "\", Pinned to: " + Arrays.toString(serviceConfiguration.getPinToNodes()));
}
}
return worker;
}