Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions .github/workflows/update_documentation.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,13 @@ jobs:
GITHUB_TOKEN: ${{ secrets.READ_PACKAGES }}

- name: Upload JavaDoc as HTML
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: JavaDoc
path: ./target/site/apidocs

- name: Upload JavaDoc as JAR
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: JavaDocJar
path: ./target/misim-javadoc.jar
Expand All @@ -50,12 +50,12 @@ jobs:

steps:
- name: Download JavaDoc Artifact
uses: actions/download-artifact@v3
uses: actions/download-artifact@v4
with:
name: JavaDoc
path: ~/docs/

- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
fetch-depth: 0

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package cambio.simulator.entities.patterns;

import cambio.simulator.entities.microservice.Microservice;
import cambio.simulator.export.MultiDataPointReporter;
import cambio.simulator.misc.TimeUtil;
import cambio.simulator.parsing.JsonTypeName;
import desmoj.core.simulator.TimeInstant;

/**
* AN implementation based on Kubernetes
* <a href="https://github.com/kubernetes/kubernetes/blob/8caeec429ee1d2a9df7b7a41b21c626346b456fb/docs/design/horizontal-pod-autoscaler.md#autoscaling-algorithm">Horizontal Pod Autoscaler</a>.
*
* <p>
* By default, scale-up can only happen if there was no rescaling within the last 3 minutes. Scale-down will wait for 5
* minutes from the last rescaling. Moreover, any scaling will only be made if: avg(CurrentPodsConsumption) / Target
* drops below 0.9 or increases above 1.1 (10% tolerance)
*
* <p>
* TODO Maybe also include via adapter, upscaling/downscaling behavior not 100% as in Kubernetes, e.g. see
* <a href="https://github.com/kubernetes/kubernetes/blob/master/pkg/apis/autoscaling/types.go#L113">HorizontalPodAutoscalerBehavior Implementation</a>
* In particular, Kubernetes mentions different holdTimes for up- and downscaling, as well as a 30s evaluation
* frequency (MiSim probably evaluates every time unit. Thus, reacts faster.)
*/
@JsonTypeName("hpa")
public class HorizontalPodAutoscalingPolicy implements IAutoscalingPolicy {
private transient MultiDataPointReporter reporter = null;
private double targetUtilization = 0.8;
private int minInstances = 1;
private int maxInstances = Integer.MAX_VALUE;

/**
* Minimum time an instance has to run before it can be shutdown by down-scaling.
*/
private double holdTime = 120;
private transient TimeInstant lastScaleUp = new TimeInstant(0);

@Override
public void apply(Microservice owner) {
if (reporter == null) {
reporter = new MultiDataPointReporter(String.format("AS[%s]_", owner.getPlainName()), owner.getModel());
}

TimeInstant presentTime = owner.presentTime();
int currentInstanceCount = owner.getInstancesCount();
double avg2Target = owner.getAverageRelativeUtilization() / targetUtilization;
// Tolerance area

int newInstanceCount = (int) Math.ceil(avg2Target * currentInstanceCount);

newInstanceCount = Math.min(newInstanceCount, maxInstances);
newInstanceCount = Math.max(minInstances, newInstanceCount);

if (currentInstanceCount < minInstances) { //starts minimum instances
owner.setInstancesCount(minInstances);
reporter.addDatapoint("Decision", presentTime, "Spawn");
reporter.addDatapoint("InstanceChange", presentTime, minInstances - currentInstanceCount);
} else if (currentInstanceCount > maxInstances) {
owner.setInstancesCount(maxInstances);
reporter.addDatapoint("Decision", presentTime, "Despawn");
reporter.addDatapoint("InstanceChange", presentTime, maxInstances - currentInstanceCount);
} else if (avg2Target > 0.9 && avg2Target < 1.1) {
// Tolerance area
reporter.addDatapoint("Decision", presentTime, "Hold");
reporter.addDatapoint("InstanceChange", presentTime, 0);
} else if (avg2Target > 1 && currentInstanceCount < maxInstances) {
owner.scaleToInstancesCount(newInstanceCount);
lastScaleUp = presentTime;
reporter.addDatapoint("Decision", presentTime, "Up");
reporter.addDatapoint("InstanceChange", presentTime, newInstanceCount - currentInstanceCount);
} else if (avg2Target < 1 && currentInstanceCount > minInstances && TimeUtil.subtract(presentTime,
lastScaleUp).getTimeAsDouble() > holdTime) {
owner.scaleToInstancesCount(newInstanceCount);
lastScaleUp = presentTime;
reporter.addDatapoint("Decision", presentTime, "Down");
reporter.addDatapoint("InstanceChange", presentTime, newInstanceCount - currentInstanceCount);
} else {
reporter.addDatapoint("Decision", presentTime, "Hold");
reporter.addDatapoint("InstanceChange", presentTime, 0);
}

if (owner.getInstancesCount() != currentInstanceCount) {
owner.sendTraceNote(String.format("Changed target instance count to %d", owner.getInstancesCount()));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,26 +39,27 @@ public void apply(Microservice owner) {
if (currentInstanceCount < minInstances) { //starts minimum instances
owner.setInstancesCount(minInstances);
reporter.addDatapoint("Decision", presentTime, "Spawn");
reporter.addDatapoint("InstanceChange", presentTime, minInstances-currentInstanceCount);
reporter.addDatapoint("InstanceChange", presentTime, minInstances - currentInstanceCount);
} else if (currentInstanceCount > maxInstances) {
owner.setInstancesCount(maxInstances);
reporter.addDatapoint("Decision", presentTime, "Despawn");
reporter.addDatapoint("InstanceChange", presentTime, maxInstances-currentInstanceCount);
reporter.addDatapoint("InstanceChange", presentTime, maxInstances - currentInstanceCount);
} else if (avg >= upperBound && currentInstanceCount < maxInstances) {
double upScalingFactor = avg / (upperBound - 0.01);
int newInstanceCount = Math.min(maxInstances, (int) Math.max(1, Math.ceil(currentInstanceCount * upScalingFactor)));
int newInstanceCount = Math.min(maxInstances, (int) Math.max(1,
Math.ceil(currentInstanceCount * upScalingFactor)));
owner.scaleToInstancesCount(newInstanceCount);
lastScaleUp = presentTime;
reporter.addDatapoint("Decision", presentTime, "Up");
reporter.addDatapoint("InstanceChange", presentTime, newInstanceCount - currentInstanceCount);
} else if (avg <= lowerBound
&& currentInstanceCount > minInstances
&& TimeUtil.subtract(presentTime, lastScaleUp).getTimeAsDouble() > holdTime) {
} else if (avg <= lowerBound && currentInstanceCount > minInstances && TimeUtil.subtract(presentTime,
lastScaleUp).getTimeAsDouble() > holdTime) {
System.out.println(presentTime);
System.out.println(lastScaleUp);
System.out.println(holdTime);
double downScaleFactor = Math.max(0.01, avg) / lowerBound;
int newInstanceCount = Math.max(minInstances, (int) Math.max(1, Math.ceil(currentInstanceCount * downScaleFactor)));
int newInstanceCount = Math.max(minInstances, (int) Math.max(1,
Math.ceil(currentInstanceCount * downScaleFactor)));
owner.scaleToInstancesCount(newInstanceCount);
lastScaleUp = presentTime;
reporter.addDatapoint("Decision", presentTime, "Down");
Expand Down