org.jppf.server.queue.BroadcastManager Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jppf-server Show documentation
Show all versions of jppf-server Show documentation
JPPF, the open source grid computing solution
The newest version!
/*
* JPPF.
* Copyright (C) 2005-2019 JPPF Team.
* http://www.jppf.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jppf.server.queue;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Lock;
import org.jppf.execute.ExecutorStatus;
import org.jppf.management.JPPFManagementInfo;
import org.jppf.node.policy.*;
import org.jppf.node.protocol.JobSLA;
import org.jppf.queue.QueueEvent;
import org.jppf.server.nio.nodeserver.BaseNodeContext;
import org.jppf.server.nio.nodeserver.async.AsyncJobScheduler;
import org.jppf.server.protocol.*;
import org.jppf.server.submission.SubmissionStatus;
import org.jppf.utils.*;
import org.jppf.utils.stats.JPPFStatisticsHelper;
import org.slf4j.*;
/**
*
* @author Laurent Cohen
*/
public class BroadcastManager {
/**
* Logger for this class.
*/
private static final Logger log = LoggerFactory.getLogger(JPPFPriorityQueue.class);
/**
* Determines whether the debug level is enabled in the logging configuration, without the cost of a method call.
*/
private static final boolean debugEnabled = LoggingUtils.isDebugEnabled(log);
/**
* A priority queue holding broadcast jobs that could not be sent due to no available connection.
*/
private final ConcurrentHashMap pendingBroadcasts = new ConcurrentHashMap<>();
/**
*
*/
private final JPPFPriorityQueue queue;
/**
*
*/
private final Lock lock;
/**
* Counts the current number of connections with ACTIVE or EXECUTING status.
*/
private final AtomicInteger nbWorkingConnections = new AtomicInteger(0);
/**
* Callback for getting all available connections. Used for processing broadcast jobs.
*/
private Callable> callableAllConnections = () -> Collections.emptyList();
/**
*
*/
private final Map jobMap;
/**
*
* @param queue the job queue.
*/
BroadcastManager(final JPPFPriorityQueue queue) {
this.queue = queue;
this.lock = queue.getLock();
this.jobMap = queue.getJobMap();
}
/**
* Set the callable source for all available connections.
* @param callableAllConnections a {@link Callable} instance.
*/
void setCallableAllConnections(final Callable> callableAllConnections) {
if (callableAllConnections == null) this.callableAllConnections = () -> Collections.emptyList();
else this.callableAllConnections = callableAllConnections;
}
/**
* Update count of working connections base on status change.
* @param oldStatus the connection status before the change.
* @param newStatus the connection status after the change.
*/
void updateWorkingConnections(final ExecutorStatus oldStatus, final ExecutorStatus newStatus) {
final boolean bNew = (newStatus == ExecutorStatus.ACTIVE) || (newStatus == ExecutorStatus.EXECUTING);
final boolean bOld = (oldStatus == ExecutorStatus.ACTIVE) || (oldStatus == ExecutorStatus.EXECUTING);
if (bNew && !bOld) nbWorkingConnections.incrementAndGet();
else if (!bNew && bOld) nbWorkingConnections.decrementAndGet();
}
/**
* Process the specified broadcast job.
* This consists in creating one job per node, each containing the same tasks,
* and with an execution policy that enforces its execution ont he designated node only.
* @param clientBundle the broadcast job to process.
*/
void processBroadcastJob(final ServerTaskBundleClient clientBundle) {
final String jobUuid = clientBundle.getUuid();
final ServerJob serverJob = jobMap.get(jobUuid);
if (serverJob == null) {
final ServerJobBroadcast broadcastJob = new ServerJobBroadcast(lock, queue.jobManager, clientBundle.getJob(), clientBundle.getDataProvider());
broadcastJob.setSubmissionStatus(SubmissionStatus.PENDING);
broadcastJob.setQueueEntryTime(System.currentTimeMillis());
broadcastJob.setJobReceivedTime(broadcastJob.getQueueEntryTime());
broadcastJob.addOnDone(new RemoveBundleAction(queue, broadcastJob));
jobMap.put(jobUuid, broadcastJob);
broadcastJob.addBundle(clientBundle);
queue.scheduleManager.handleStartJobSchedule(broadcastJob);
queue.scheduleManager.handleExpirationJobSchedule(queue.driver, broadcastJob);
queue.jobManager.jobQueued(broadcastJob);
pendingBroadcasts.put(jobUuid, broadcastJob);
//processPendingBroadcasts();
} else serverJob.addBundle(clientBundle);
}
/**
* Cancels queued broadcast jobs for connection.
* @param connectionUUID The connection UUID that failed or was disconnected.
*/
public void cancelBroadcastJobs(final String connectionUUID) {
if (connectionUUID == null || connectionUUID.isEmpty()) return;
Set jobIDs = Collections.emptySet();
lock.lock();
try {
if (jobMap.isEmpty()) return;
jobIDs = new HashSet<>();
for (Map.Entry entry : jobMap.entrySet()) {
if (connectionUUID.equals(entry.getValue().getBroadcastUUID())) jobIDs.add(entry.getKey());
}
} finally {
lock.unlock();
}
for (String jobID : jobIDs) queue.cancelJob(jobID);
}
/**
* Process the jobs in the pending broadcast queue.
* This method is normally called from TaskQueueChecker.dispatch()
.
*/
public void processPendingBroadcasts() {
if (nbWorkingConnections.get() <= 0) return;
List connections;
try {
connections = callableAllConnections.call();
} catch (@SuppressWarnings("unused") final Throwable e) {
connections = Collections.emptyList();
}
if (connections.isEmpty()) return;
for (final Map.Entry entry: pendingBroadcasts.entrySet()) {
final ServerJobBroadcast broadcastJob = entry.getValue();
if (debugEnabled) log.debug("queuing job " + broadcastJob.getJob());
processPendingBroadcast(connections, broadcastJob);
}
}
/**
* Dispatch broadcast job to all available ACTIVE and EXECUTING connections.
* @param connections the list of all available connections.
* @param broadcastJob the job to dispatch to connections.
*/
private void processPendingBroadcast(final List connections, final ServerJobBroadcast broadcastJob) {
if (broadcastJob == null) throw new IllegalArgumentException("broadcastJob is null");
if (pendingBroadcasts.remove(broadcastJob.getUuid()) == null) return;
final JobSLA sla = broadcastJob.getSLA();
final List jobList = new ArrayList<>(connections.size());
final Set uuidSet = new HashSet<>();
for (final BaseNodeContext connection : connections) {
final ExecutorStatus status = connection.getExecutionStatus();
if (status == ExecutorStatus.ACTIVE || status == ExecutorStatus.EXECUTING) {
final String uuid = connection.getUuid();
if (uuid != null && uuid.length() > 0 && uuidSet.add(uuid)) {
final JPPFManagementInfo info = connection.getManagementInfo();
final ExecutionPolicy policy = sla.getExecutionPolicy();
AsyncJobScheduler.preparePolicy(policy, broadcastJob, queue.driver.getStatistics(), 0);
if ((policy != null) && !policy.evaluate(info.getSystemInfo())) {
if (debugEnabled) log.debug("node uuid={} refused for broadcast {}", uuid, broadcastJob);
continue;
}
ExecutionPolicy broadcastPolicy = new Equal("jppf.uuid", true, uuid);
if (policy != null) broadcastPolicy = broadcastPolicy.and(policy);
final ServerJobBroadcast newBundle = broadcastJob.createBroadcastJob(uuid);
newBundle.setSLA(sla.copy());
newBundle.setMetadata(broadcastJob.getMetadata());
newBundle.getSLA().setExecutionPolicy(broadcastPolicy);
newBundle.setName(broadcastJob.getName() + " [node: " + info.toDisplayString() + ']');
newBundle.setUuid(JPPFUuid.normalUUID());
jobList.add(newBundle);
if (debugEnabled) log.debug("node uuid={} accepted for broadcast: {}, execution policy =\n{}", uuid, newBundle, newBundle.getSLA().getExecutionPolicy());
}
}
}
if (jobList.isEmpty()) broadcastJob.jobEnded();
else {
lock.lock();
try {
queue.fireBundleAdded(new QueueEvent<>(queue, broadcastJob, false));
for (ServerJobBroadcast job : jobList) addBroadcastJob(job);
} finally {
lock.unlock();
}
}
}
/**
* Add an broadcast job to the queue, and notify all listeners about it.
* @param broadcastJob the job with assigned broadcast id to add to the queue.
*/
private void addBroadcastJob(final ServerJobBroadcast broadcastJob) {
if (broadcastJob == null) throw new IllegalArgumentException("broadcastJob is null");
final String jobUuid = broadcastJob.getUuid();
broadcastJob.setSubmissionStatus(SubmissionStatus.PENDING);
broadcastJob.setQueueEntryTime(System.currentTimeMillis());
broadcastJob.setJobReceivedTime(broadcastJob.getQueueEntryTime());
broadcastJob.addOnDone(new RemoveBundleAction(queue, broadcastJob));
queue.getPriorityMap().putValue(broadcastJob.getSLA().getPriority(), broadcastJob);
if (debugEnabled) log.debug("adding bundle with " + broadcastJob);
queue.scheduleManager.handleStartJobSchedule(broadcastJob);
queue.scheduleManager.handleExpirationJobSchedule(queue.driver, broadcastJob);
jobMap.put(jobUuid, broadcastJob);
queue.updateLatestMaxSize();
queue.jobManager.jobQueued(broadcastJob);
queue.fireBundleAdded(new QueueEvent<>(queue, broadcastJob, false));
queue.driver.getStatistics().addValue(JPPFStatisticsHelper.TASK_QUEUE_COUNT, broadcastJob.getTaskCount());
}
}