package codeine.command_peer; import java.util.List; import java.util.concurrent.TimeUnit; import org.apache.log4j.Logger; import codeine.api.NodeGetter; import codeine.api.NodeWithMonitorsInfo; import codeine.api.NodeWithPeerInfo; import codeine.api.ScehudleCommandExecutionInfo; import codeine.configuration.Links; import codeine.jsons.project.ProjectJson; import codeine.permissions.IUserWithPermissions; import codeine.utils.StringUtils; import codeine.utils.ThreadUtils; import com.google.common.collect.Lists; public class ProgressiveExecutionStrategy extends CommandExecutionStrategy { private static final Logger log = Logger.getLogger(ProgressiveExecutionStrategy.class); private NodeGetter nodesGetter; private Object cancelObject = new Object(); public ProgressiveExecutionStrategy(AllNodesCommandExecuter allNodesCommandExecuter,ScehudleCommandExecutionInfo commandData, Links links, NodeGetter nodesGetter, ProjectJson project, IUserWithPermissions userObject) { super(commandData, allNodesCommandExecuter, links, project, userObject); this.nodesGetter = nodesGetter; } @Override public void execute() { List<NodeWithPeerInfo> leftNodes = Lists.newArrayList(commandData().nodes()); long endTime = System.currentTimeMillis() + durationInMillis(); ProgressiveRateClaculator calc; switch (commandData().command_info().ratio()){ case Linear: { calc = new LinearProgressiveRateClaculator(); break; } case Exponential: { calc = new ExponentialProgressiveRateClaculator(leftNodes.size(), durationInMillis()); break; } default: throw new RuntimeException("no ratio implementation: " + commandData().command_info().ratio()); } List<NodeWithPeerInfo> completedNodes = Lists.newArrayList(); while (leftNodes.size() > 0 && !isCancel()) { if (commandData().command_info().stop_on_error() && completedNodes.size() > 0) { List<NodeWithMonitorsInfo> nodes = nodesGetter.getNodes(commandData().command_info().project_name(), completedNodes); List<String> failedNodes = Lists.newArrayList(); for (NodeWithMonitorsInfo nodeInfo : nodes) { if (!nodeInfo.status()){ failedNodes.add(nodeInfo.alias()); } } int errorPercent = failedNodes.size() * 100 / completedNodes.size(); if (errorPercent > commandData().command_info().error_percent_val()) { writeLine("nodes with error: " + failedNodes); writeLine("Execution stopped because of errors. error percent is " + errorPercent + "% number of nodes with error: " + failedNodes.size()); return; } } long startLoop = System.currentTimeMillis(); double minutesLeft = (double) TimeUnit.MILLISECONDS.toMinutes(endTime - System.currentTimeMillis()); calc.iterationStart(minutesLeft, leftNodes.size()); int numOfNodesToExecute = calc.numOfNodesToExecute(); writeLine("Will execute on " + numOfNodesToExecute + " nodes"); if (numOfNodesToExecute > CommandExecutionStrategy.MAX_NODES_TO_EXECUTE) { log.info("numOfNodesToExecute is above limit " + numOfNodesToExecute); writeLine("execution concurrency is above limit, will set it to " + CommandExecutionStrategy.MAX_NODES_TO_EXECUTE); numOfNodesToExecute = CommandExecutionStrategy.MAX_NODES_TO_EXECUTE; } List<NodeWithPeerInfo> subList = leftNodes.subList(0, numOfNodesToExecute); executeConcurrent(subList, numOfNodesToExecute); completedNodes.addAll(subList); subList.clear(); long loopTime = System.currentTimeMillis() - startLoop; long sleepTime = calc.getTimeToSleep(loopTime); if ((sleepTime > 0) && (leftNodes.size() > 0)) { writeLine("Execution of " + numOfNodesToExecute + " nodes took " + StringUtils.formatTimePeriod(loopTime) + ", going to sleep for " + StringUtils.formatTimePeriod(sleepTime)); ThreadUtils.wait(cancelObject, sleepTime); } else { writeLine("Execution of " + numOfNodesToExecute + " nodes took " + StringUtils.formatTimePeriod(loopTime) + ", will not go to sleep"); } } log.info("done executing"); } private long durationInMillis() { switch (commandData().command_info().duration_units()) { case Days: return TimeUnit.DAYS.toMillis(commandData().command_info().duration()); case Hours: return TimeUnit.HOURS.toMillis(commandData().command_info().duration()); case Minutes: return TimeUnit.MINUTES.toMillis(commandData().command_info().duration()); default: throw new UnsupportedOperationException("unknown duration units " + commandData().command_info().duration_units()); } } @Override public void setCancel() { super.setCancel(); synchronized (cancelObject) { cancelObject.notifyAll(); } } }