package com.griddynamics.jagger.engine.e1.scenario;
import com.google.common.collect.Maps;
import com.griddynamics.jagger.coordinator.NodeId;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.*;
public class ExactInvocationsClock implements WorkloadClock {
private static final Logger log = LoggerFactory.getLogger(ExactInvocationsClock.class);
private int threadCount;
private int samplesCount;
private int samplesSubmitted;
private int delay;
private int tickInterval;
private long period;
public ExactInvocationsClock(int samplesCount, int threadCount, int delay, int tickInterval, long period) {
this.samplesCount = samplesCount;
this.threadCount = threadCount;
this.delay = delay;
this.tickInterval = tickInterval;
this.period = period;
}
@Override
public Map<NodeId, Integer> getPoolSizes(Set<NodeId> nodes) {
int max = threadCount / nodes.size() + 1;
Map<NodeId, Integer> result = Maps.newHashMap();
for (NodeId node : nodes) {
result.put(node, max);
}
return result;
}
private long startTime = 0;
@Override
public void tick(WorkloadExecutionStatus status, WorkloadAdjuster adjuster) {
log.debug("Going to perform tick with status {}", status);
if (isPeriodic()) {
long currentTime = System.currentTimeMillis();
if (startTime == 0) {
startTime = currentTime - period;
}
long difference = currentTime - startTime;
if(difference >= period) {
if (status.getTotalSamples() < samplesSubmitted) {
log.warn("Can not create such load with {} invocations with {} ms period", samplesCount, period);
}
startTime = startTime + period;
sendSamples(samplesCount, status, adjuster);
}
} else {
int samplesLeft = samplesCount - samplesSubmitted;
sendSamples(samplesLeft, status, adjuster);
}
}
private void sendSamples(int samplesLeft, WorkloadExecutionStatus status, WorkloadAdjuster adjuster) {
if (samplesLeft <= 0) {
return;
}
Set<NodeId> nodes = status.getNodes();
int nodesSize = nodes.size();
int threadsForOneNode = threadCount / nodesSize; // how many threads should be distributed for one node(kernel)
int threadsResidue = threadCount % nodesSize; // residue of threads
int samplesForOneThread = samplesLeft / threadCount; //how many samples should be distributed for ont thread
int samplesResidueByThreads = samplesLeft % threadCount; // residue samples of threads
int additionalSamplesForOneNode = samplesResidueByThreads / nodesSize; // how many samples should be added for each node(kernel)
int samplesResidue = samplesResidueByThreads % nodesSize; // residue samples of nodes
int s = 0;
for (NodeId node : nodes) {
int curSamples = status.getSamples(node);
int curThreads = threadsForOneNode;
if (threadsResidue > 0) {
curThreads ++;
threadsResidue --;
}
curSamples += samplesForOneThread * curThreads + additionalSamplesForOneNode;
if (samplesResidue > 0) {
curSamples ++;
samplesResidue --;
}
WorkloadConfiguration workloadConfiguration = WorkloadConfiguration.with(curThreads, delay, curSamples);
adjuster.adjustConfiguration(node, workloadConfiguration);
s += curSamples;
}
samplesSubmitted = s;
}
private boolean isPeriodic() {
return period != -1;
}
@Override
public int getTickInterval() {
return tickInterval;
}
@Override
public int getValue() {
return samplesCount;
}
@Override
public String toString() {
return threadCount + " virtual users";
}
}