/*
* Copyright (C) 2006-2016 DLR, Germany
*
* All rights reserved
*
* http://www.rcenvironment.de/
*/
package de.rcenvironment.core.communication.management.internal;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import org.apache.commons.logging.LogFactory;
import de.rcenvironment.core.communication.common.InstanceNodeSessionId;
import de.rcenvironment.core.communication.management.BenchmarkSubtask;
import de.rcenvironment.core.utils.common.StringUtils;
import de.rcenvironment.core.utils.common.rpc.RemoteOperationException;
/**
* Default {@link BenchmarkSubtask} implementation.
*
* @author Robert Mischke
*/
public class BenchmarkSubtaskImpl implements BenchmarkSubtask {
/**
* Default/fallback value when there is no actual result, for example the "average duration on success" when all requests have failed.
*/
private static final int NO_DURATION_AVAILABLE_VALUE = -1;
private final List<InstanceNodeSessionId> targetNodes;
private final int numMessages;
private final int requestSize;
private final int responseSize;
private final int responseDelay;
private final int threadsPerTarget;
private Map<InstanceNodeSessionId, NodeResultContainer> nodeResults;
private long subtaskStartTime;
private CountDownLatch subtaskRequestCountdown;
/**
* Holds the benchmark results for a single target node.
*
* @author Robert Mischke
*/
private class NodeResultContainer {
private long totalTime;
private int numSuccess;
private int numFinished;
private long totalDuration;
private long totalSuccessfulTime;
private long totalFailureTime;
long getTotalTime() {
return totalTime;
}
void setTotalTime(long totalTime) {
this.totalTime = totalTime;
}
int getNumSuccess() {
return numSuccess;
}
void setNumSuccess(int numSuccess) {
this.numSuccess = numSuccess;
}
int getNumFinished() {
return numFinished;
}
void setNumFinished(int numFinished) {
this.numFinished = numFinished;
}
long getTotalDuration() {
return totalDuration;
}
void setTotalDuration(long totalDuration) {
this.totalDuration = totalDuration;
}
long getTotalSuccessfulTime() {
return totalSuccessfulTime;
}
void setTotalSuccessfulTime(long totalSuccessfulTime) {
this.totalSuccessfulTime = totalSuccessfulTime;
}
long getTotalFailureTime() {
return totalFailureTime;
}
void setTotalFailureTime(long totalFailureTime) {
this.totalFailureTime = totalFailureTime;
}
}
public BenchmarkSubtaskImpl(List<InstanceNodeSessionId> targetNodes, int numMessages, int requestSize, int responseSize,
int responseDelay, int threadsPerTarget) {
this.targetNodes = Collections.unmodifiableList(targetNodes);
this.numMessages = numMessages;
this.requestSize = requestSize;
this.responseSize = responseSize;
this.responseDelay = responseDelay;
this.threadsPerTarget = threadsPerTarget;
this.nodeResults = Collections.synchronizedMap(new HashMap<InstanceNodeSessionId, BenchmarkSubtaskImpl.NodeResultContainer>());
for (InstanceNodeSessionId node : targetNodes) {
nodeResults.put(node, new NodeResultContainer());
}
subtaskRequestCountdown = new CountDownLatch(numMessages * targetNodes.size());
}
/**
* {@inheritDoc}
*
* @see de.rcenvironment.core.communication.management.BenchmarkSubtask#getTargetNodes()
*/
@Override
public List<InstanceNodeSessionId> getTargetNodes() {
return targetNodes;
}
@Override
public int getNumMessages() {
return numMessages;
}
@Override
public int getRequestSize() {
return requestSize;
}
@Override
public int getResponseSize() {
return responseSize;
}
@Override
public int getResponseDelay() {
return responseDelay;
}
@Override
public int getThreadsPerTarget() {
return threadsPerTarget;
}
/**
* @return a formatted string representing the subtask
*/
public String formatDescription() {
StringBuilder builder = new StringBuilder();
boolean first = true;
for (InstanceNodeSessionId node : targetNodes) {
if (!first) {
builder.append(", ");
} else {
first = false;
}
builder.append(node);
}
String targetDescr = builder.toString();
return StringUtils.format(
"target nodes: {%s}, requests per target: %d, request size: %d, response size: %d, "
+ "response delay: %dms, threads per target: %d",
targetDescr, numMessages, requestSize, responseSize, responseDelay, threadsPerTarget);
}
public long getRemainingRequestCount() {
return subtaskRequestCountdown.getCount();
}
/**
* Blocks until this subtasks has completed.
*
* @throws InterruptedException on interruption
*/
public void awaitTermination() throws InterruptedException {
// TODO provide variant with timeout?
subtaskRequestCountdown.await();
}
/**
* @return an array of formatted strings representing the result of this subtask
*/
public String[] formatResults() {
String[] array = new String[targetNodes.size()];
int i = 0;
for (InstanceNodeSessionId targetNode : targetNodes) {
NodeResultContainer resultContainer = nodeResults.get(targetNode);
// synchronize for thread visibility
synchronized (resultContainer) {
long timeToFinish = toMsec(resultContainer.getTotalDuration());
long avgRawSuccessMsec = NO_DURATION_AVAILABLE_VALUE;
long avgActualSuccessMsec = NO_DURATION_AVAILABLE_VALUE;
if (resultContainer.getNumSuccess() > 0) {
avgRawSuccessMsec = toMsec(resultContainer.getTotalSuccessfulTime() / resultContainer.getNumSuccess());
avgActualSuccessMsec = avgRawSuccessMsec - responseDelay;
}
int numFailures = numMessages - resultContainer.getNumSuccess();
array[i++] =
StringUtils.format(
"%s: Avg actual time: %d ms, Avg raw time: %d ms, Failures: %d, Total time: %d ms",
targetNode, avgActualSuccessMsec, avgRawSuccessMsec, numFailures, timeToFinish);
}
}
return array;
}
private long toMsec(long duration) {
return TimeUnit.MILLISECONDS.convert(duration, TimeUnit.NANOSECONDS);
}
void recordStartTime() {
subtaskStartTime = System.nanoTime();
}
/**
* Records the outcome of a single benchmark request.
*
* @param targetNode the target node thas was contacted
* @param duration the total duration
* @param error null on normal completion, or the generated exception
*/
public void recordSingleResult(InstanceNodeSessionId targetNode, long duration, RemoteOperationException error) {
NodeResultContainer resultContainer = nodeResults.get(targetNode);
synchronized (resultContainer) {
resultContainer.setNumFinished(resultContainer.getNumFinished() + 1);
resultContainer.setTotalTime(resultContainer.getTotalTime() + duration);
if (error == null) {
resultContainer.setNumSuccess(resultContainer.getNumSuccess() + 1);
resultContainer.setTotalSuccessfulTime(resultContainer.getTotalSuccessfulTime() + duration);
} else {
LogFactory.getLog(getClass()).warn("Error on benchmark request: " + error.getMessage());
resultContainer.setTotalFailureTime(resultContainer.getTotalFailureTime() + duration);
}
if (resultContainer.getNumFinished() == numMessages) {
resultContainer.setTotalDuration(System.nanoTime() - subtaskStartTime);
}
}
subtaskRequestCountdown.countDown();
}
}