Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: improve error handling, simplify code #87

Merged
merged 2 commits into from
Jul 11, 2024
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
4 changes: 2 additions & 2 deletions .github/project.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
release:
current-version: "0.0.2"
next-version: "0.0.3-SNAPSHOT"
current-version: "0.0.3"
next-version: "0.0.4-SNAPSHOT"

Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
package net.laprun.sustainability.power.quarkus.deployment.devui.commands;

import net.laprun.sustainability.power.measure.PowerMeasure;
import org.aesh.command.CommandDefinition;
import org.aesh.command.CommandResult;
import org.aesh.command.invocation.CommandInvocation;
import org.aesh.command.option.Option;

import io.quarkus.deployment.console.QuarkusCommand;
import net.laprun.sustainability.power.measure.PowerMeasure;
import net.laprun.sustainability.power.quarkus.runtime.PowerMeasurer;

@CommandDefinition(name = "start", description = "Starts measuring power consumption of the current application")
Expand Down Expand Up @@ -39,24 +39,12 @@ public CommandResult doExecute(CommandInvocation commandInvocation) {
if (baseline == null) {
commandInvocation.println("Establishing baseline for 30s, please do not use your application until done.");
commandInvocation.println("Power measurement will start as configured after this initial measure is done.");
sensor.start(30, 1000);
sensor.onError(e -> commandInvocation.println("An error occurred: " + e.getMessage()));
sensor.onCompleted((m) -> {
baseline = m;
outputConsumptionSinceStarted(baseline, commandInvocation, true);
commandInvocation.println("Baseline established! You can now interact with your application normally.");

try {
sensor.start(duration, frequency);
} catch (Exception e) {
throw new RuntimeException(e);
}
sensor.onCompleted(
(finished) -> outputConsumptionSinceStarted(finished, commandInvocation, false));
});
sensor.withCompletedHandler((m) -> establishBaseline(commandInvocation, m))
.withErrorHandler(e -> commandInvocation.println("An error occurred: " + e.getMessage()))
.start(30, 1000);
} else {
sensor.start(duration, frequency);
sensor.onCompleted((m) -> outputConsumptionSinceStarted(m, commandInvocation, false));
sensor.withCompletedHandler((m) -> outputConsumptionSinceStarted(m, commandInvocation, false))
.start(duration, frequency);
}

} else {
Expand All @@ -69,11 +57,24 @@ public CommandResult doExecute(CommandInvocation commandInvocation) {
return CommandResult.SUCCESS;
}

private void establishBaseline(CommandInvocation commandInvocation, PowerMeasure m) {
baseline = m;
outputConsumptionSinceStarted(baseline, commandInvocation, true);
commandInvocation
.println("Baseline established! You can now interact with your application normally.");
try {
sensor.start(duration, frequency);
} catch (Exception e) {
throw new RuntimeException(e);
}
sensor.withCompletedHandler(
(finished) -> outputConsumptionSinceStarted(finished, commandInvocation, false));
}

private void outputConsumptionSinceStarted(PowerMeasure measure, CommandInvocation out, boolean isBaseline) {
final var title = isBaseline ? "\nBaseline => " : "\nMeasured => ";
out.println(title + PowerMeasure.asString(measure));
if (!isBaseline) {
sensor.additionalSensorInfo().ifPresent(out::println);
out.println("Baseline => " + PowerMeasure.asString(baseline));
out.println("Average ∆ => " + PowerMeasure.readableWithUnit(measure.average() - baseline.average()));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public StopCommand(PowerMeasurer sensor) {

@Override
public CommandResult doExecute(CommandInvocation commandInvocation) {
sensor.onError(e -> commandInvocation.println("An error occurred: " + e.getMessage()));
sensor.withErrorHandler(e -> commandInvocation.println("An error occurred: " + e.getMessage()));
if (sensor.isRunning()) {
sensor.stop();
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,6 @@ void startShouldAccumulateOverSpecifiedDurationAndStop() {
final var measurer = new PowerMeasurer(new ServerSampler(uri));

measurer.start(1, 100);
measurer.onCompleted(measure -> assertEquals(10, measure.numberOfSamples()));
measurer.withCompletedHandler(measure -> assertEquals(10, measure.numberOfSamples()));
}
}
2 changes: 1 addition & 1 deletion docs/modules/ROOT/pages/includes/attributes.adoc
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
:project-version: 0.0.2
:project-version: 0.0.3

:examples-dir: ./../examples/
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
package net.laprun.sustainability.power.quarkus.runtime;

import java.lang.management.ManagementFactory;
import java.util.Optional;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.function.BiConsumer;
import java.util.function.Consumer;

import com.sun.management.OperatingSystemMXBean;
Expand All @@ -25,9 +23,6 @@ public class PowerMeasurer {

private final ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
private final Sampler sampler;
private Consumer<PowerMeasure> completed;
private BiConsumer<Integer, PowerMeasure> sampled;
private Consumer<Exception> errorHandler;
private static PowerMeasurer instance;

public static PowerMeasurer instance() {
Expand All @@ -43,7 +38,7 @@ private PowerMeasurer() {

public PowerMeasurer(Sampler sampler) {
this.sampler = sampler;
this.onError(null);
this.withErrorHandler(null);
}

@SuppressWarnings("unused")
Expand All @@ -53,57 +48,34 @@ public static double cpuShareOfJVMProcess() {
return (processCpuLoad < 0 || cpuLoad <= 0) ? 0 : processCpuLoad / cpuLoad;
}

public void onCompleted(Consumer<PowerMeasure> completed) {
this.completed = completed;
public PowerMeasurer withCompletedHandler(Consumer<PowerMeasure> completed) {
sampler.withCompletedHandler(completed);
return this;
}

public void onSampled(BiConsumer<Integer, PowerMeasure> sampled) {
this.sampled = sampled;
}

public void onError(Consumer<Exception> errorHandler) {
this.errorHandler = errorHandler != null ? errorHandler : (exception) -> {
public PowerMeasurer withErrorHandler(Consumer<Throwable> errorHandler) {
errorHandler = errorHandler != null ? errorHandler : (exception) -> {
throw new RuntimeException(exception);
};
}

public Optional<String> additionalSensorInfo() {
// return Optional.ofNullable(measure).flatMap(sensor::additionalInfo);
return Optional.empty(); // fixme
sampler.withErrorHandler(errorHandler);
return this;
}

public boolean isRunning() {
return sampler.isRunning();
}

public void start(long durationInSeconds, long frequencyInMilliseconds) {
try {
sampler.start(durationInSeconds, frequencyInMilliseconds);

if (durationInSeconds > 0) {
executor.schedule(this::stop, durationInSeconds, TimeUnit.SECONDS);
}
} catch (Exception e) {
handleError(e);
}
}
sampler.start(durationInSeconds, frequencyInMilliseconds);

private void handleError(Exception e) {
errorHandler.accept(e);
try {
sampler.stop(completed);
} catch (Exception ex) {
// ignore shutting down exceptions
if (durationInSeconds > 0) {
executor.schedule(this::stop, durationInSeconds, TimeUnit.SECONDS);
}
}

public void stop() {
try {
if (isRunning()) {
sampler.stop(completed);
}
} catch (Exception e) {
handleError(e);
if (isRunning()) {
sampler.stop();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,15 @@ public interface Sampler {

boolean isRunning();

void start(long durationInSeconds, long frequencyInMilliseconds) throws Exception;
void start(long durationInSeconds, long frequencyInMilliseconds);

void stop(Consumer<PowerMeasure> completed);
void stop();

void stopOnError(Throwable e);

@SuppressWarnings("UnusedReturnValue")
Sampler withCompletedHandler(Consumer<PowerMeasure> completed);

@SuppressWarnings("UnusedReturnValue")
Sampler withErrorHandler(Consumer<Throwable> errorHandler);
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ public class ServerSampler implements Sampler {
private OngoingPowerMeasure measure;
private SensorMetadata metadata;
private static final long pid = ProcessHandle.current().pid();
private Consumer<PowerMeasure> completed;
private Consumer<Throwable> errorHandler;

@ConfigProperty(name = "power-server.url", defaultValue = "http://localhost:20432")
URI powerServerURI;
Expand All @@ -45,7 +47,7 @@ public ServerSampler(URI powerServerURI) {

final var powerForPid = base.path("{pid}").resolveTemplate("pid", pid);
powerAPI = SseEventSource.target(powerForPid).build();
powerAPI.register(this::update, (e) -> System.out.println("Exception: " + e.getMessage()));
powerAPI.register(this::onEvent, this::stopOnError, this::onComplete);
}

public ServerSampler() {
Expand All @@ -69,21 +71,33 @@ public void start(long durationInSeconds, long frequencyInMilliseconds) {
if (e instanceof ProcessingException processingException) {
final var cause = processingException.getCause();
if (cause instanceof ConnectException connectException) {
throw new RuntimeException(
stopOnError(new RuntimeException(
"Couldn't connect to power-server. Please see the instructions to set it up and run it.",
connectException);
connectException));
}
}
throw new RuntimeException(e);
stopOnError(e);
}
}

private void update(InboundSseEvent event) {
@Override
public void stopOnError(Throwable e) {
errorHandler.accept(e);
if(measure != null && measure.numberOfSamples() > 0) {
stop();
}
}

private void onComplete() {
System.out.println("Finished!");
}

private void onEvent(InboundSseEvent event) {
final var measureFromServer = event.readData(SensorMeasure.class);
update(measureFromServer);
record(measureFromServer);
}

private void update(SensorMeasure measureFromServer) {
private void record(SensorMeasure measureFromServer) {
if (measureFromServer != null) {
final var components = measureFromServer.components();
if (Arrays.equals(new double[] { -1.0 }, components)) {
Expand All @@ -98,10 +112,22 @@ private void update(SensorMeasure measureFromServer) {
}
}

public void stop(Consumer<PowerMeasure> completed) {
public void stop() {
powerAPI.close();
final var measured = new StoppedPowerMeasure(measure);
measure = null;
completed.accept(measured);
}

@Override
public Sampler withCompletedHandler(Consumer<PowerMeasure> completed) {
this.completed = completed;
return this;
}

@Override
public Sampler withErrorHandler(Consumer<Throwable> errorHandler) {
this.errorHandler = errorHandler;
return this;
}
}
Loading