From 21507d0732400af07822e3c92a0c8a118b3d0ae1 Mon Sep 17 00:00:00 2001 From: Chris Laprun Date: Thu, 11 Jul 2024 22:10:42 +0200 Subject: [PATCH 1/2] feat: improve error handling, simplify --- .../devui/commands/StartCommand.java | 39 +++++++------- .../devui/commands/StopCommand.java | 2 +- .../quarkus/deployment/PowerMeasurerTest.java | 2 +- .../power/quarkus/runtime/PowerMeasurer.java | 54 +++++-------------- .../power/quarkus/runtime/Sampler.java | 12 ++++- .../power/quarkus/runtime/ServerSampler.java | 44 ++++++++++++--- 6 files changed, 81 insertions(+), 72 deletions(-) diff --git a/deployment/src/main/java/net/laprun/sustainability/power/quarkus/deployment/devui/commands/StartCommand.java b/deployment/src/main/java/net/laprun/sustainability/power/quarkus/deployment/devui/commands/StartCommand.java index 0179a49..a8f5b13 100644 --- a/deployment/src/main/java/net/laprun/sustainability/power/quarkus/deployment/devui/commands/StartCommand.java +++ b/deployment/src/main/java/net/laprun/sustainability/power/quarkus/deployment/devui/commands/StartCommand.java @@ -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") @@ -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 { @@ -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())); } diff --git a/deployment/src/main/java/net/laprun/sustainability/power/quarkus/deployment/devui/commands/StopCommand.java b/deployment/src/main/java/net/laprun/sustainability/power/quarkus/deployment/devui/commands/StopCommand.java index 4e86812..eb1f864 100644 --- a/deployment/src/main/java/net/laprun/sustainability/power/quarkus/deployment/devui/commands/StopCommand.java +++ b/deployment/src/main/java/net/laprun/sustainability/power/quarkus/deployment/devui/commands/StopCommand.java @@ -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 { diff --git a/deployment/src/test/java/net/laprun/sustainability/power/quarkus/deployment/PowerMeasurerTest.java b/deployment/src/test/java/net/laprun/sustainability/power/quarkus/deployment/PowerMeasurerTest.java index c639cae..98ea9d3 100644 --- a/deployment/src/test/java/net/laprun/sustainability/power/quarkus/deployment/PowerMeasurerTest.java +++ b/deployment/src/test/java/net/laprun/sustainability/power/quarkus/deployment/PowerMeasurerTest.java @@ -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())); } } diff --git a/runtime/src/main/java/net/laprun/sustainability/power/quarkus/runtime/PowerMeasurer.java b/runtime/src/main/java/net/laprun/sustainability/power/quarkus/runtime/PowerMeasurer.java index 08f9939..3b57229 100644 --- a/runtime/src/main/java/net/laprun/sustainability/power/quarkus/runtime/PowerMeasurer.java +++ b/runtime/src/main/java/net/laprun/sustainability/power/quarkus/runtime/PowerMeasurer.java @@ -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; @@ -25,9 +23,6 @@ public class PowerMeasurer { private final ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor(); private final Sampler sampler; - private Consumer completed; - private BiConsumer sampled; - private Consumer errorHandler; private static PowerMeasurer instance; public static PowerMeasurer instance() { @@ -43,7 +38,7 @@ private PowerMeasurer() { public PowerMeasurer(Sampler sampler) { this.sampler = sampler; - this.onError(null); + this.withErrorHandler(null); } @SuppressWarnings("unused") @@ -53,23 +48,17 @@ public static double cpuShareOfJVMProcess() { return (processCpuLoad < 0 || cpuLoad <= 0) ? 0 : processCpuLoad / cpuLoad; } - public void onCompleted(Consumer completed) { - this.completed = completed; + public PowerMeasurer withCompletedHandler(Consumer completed) { + sampler.withCompletedHandler(completed); + return this; } - public void onSampled(BiConsumer sampled) { - this.sampled = sampled; - } - - public void onError(Consumer errorHandler) { - this.errorHandler = errorHandler != null ? errorHandler : (exception) -> { + public PowerMeasurer withErrorHandler(Consumer errorHandler) { + errorHandler = errorHandler != null ? errorHandler : (exception) -> { throw new RuntimeException(exception); }; - } - - public Optional additionalSensorInfo() { - // return Optional.ofNullable(measure).flatMap(sensor::additionalInfo); - return Optional.empty(); // fixme + sampler.withErrorHandler(errorHandler); + return this; } public boolean isRunning() { @@ -77,33 +66,16 @@ public boolean 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(); } } } diff --git a/runtime/src/main/java/net/laprun/sustainability/power/quarkus/runtime/Sampler.java b/runtime/src/main/java/net/laprun/sustainability/power/quarkus/runtime/Sampler.java index b7818ad..2b66da2 100644 --- a/runtime/src/main/java/net/laprun/sustainability/power/quarkus/runtime/Sampler.java +++ b/runtime/src/main/java/net/laprun/sustainability/power/quarkus/runtime/Sampler.java @@ -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 completed); + void stop(); + + void stopOnError(Throwable e); + + @SuppressWarnings("UnusedReturnValue") + Sampler withCompletedHandler(Consumer completed); + + @SuppressWarnings("UnusedReturnValue") + Sampler withErrorHandler(Consumer errorHandler); } diff --git a/runtime/src/main/java/net/laprun/sustainability/power/quarkus/runtime/ServerSampler.java b/runtime/src/main/java/net/laprun/sustainability/power/quarkus/runtime/ServerSampler.java index 3512f24..30b106c 100644 --- a/runtime/src/main/java/net/laprun/sustainability/power/quarkus/runtime/ServerSampler.java +++ b/runtime/src/main/java/net/laprun/sustainability/power/quarkus/runtime/ServerSampler.java @@ -4,6 +4,7 @@ import java.net.URI; import java.time.Duration; import java.util.Arrays; +import java.util.function.BiConsumer; import java.util.function.Consumer; import jakarta.ws.rs.ProcessingException; @@ -30,6 +31,9 @@ public class ServerSampler implements Sampler { private OngoingPowerMeasure measure; private SensorMetadata metadata; private static final long pid = ProcessHandle.current().pid(); + private Consumer completed; + private Consumer errorHandler; + private BiConsumer sampled; @ConfigProperty(name = "power-server.url", defaultValue = "http://localhost:20432") URI powerServerURI; @@ -45,7 +49,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() { @@ -69,21 +73,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)) { @@ -98,10 +114,22 @@ private void update(SensorMeasure measureFromServer) { } } - public void stop(Consumer completed) { + public void stop() { powerAPI.close(); final var measured = new StoppedPowerMeasure(measure); measure = null; completed.accept(measured); } + + @Override + public Sampler withCompletedHandler(Consumer completed) { + this.completed = completed; + return this; + } + + @Override + public Sampler withErrorHandler(Consumer errorHandler) { + this.errorHandler = errorHandler; + return this; + } } From 90239ffb6d70bd3851f801c11a236835f3955108 Mon Sep 17 00:00:00 2001 From: Chris Laprun Date: Thu, 11 Jul 2024 22:12:05 +0200 Subject: [PATCH 2/2] chore: release 0.0.3 --- .github/project.yml | 4 ++-- docs/modules/ROOT/pages/includes/attributes.adoc | 2 +- .../sustainability/power/quarkus/runtime/ServerSampler.java | 2 -- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/.github/project.yml b/.github/project.yml index cb011b9..46caa2e 100644 --- a/.github/project.yml +++ b/.github/project.yml @@ -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" diff --git a/docs/modules/ROOT/pages/includes/attributes.adoc b/docs/modules/ROOT/pages/includes/attributes.adoc index 46b41ac..cb56961 100644 --- a/docs/modules/ROOT/pages/includes/attributes.adoc +++ b/docs/modules/ROOT/pages/includes/attributes.adoc @@ -1,3 +1,3 @@ -:project-version: 0.0.2 +:project-version: 0.0.3 :examples-dir: ./../examples/ \ No newline at end of file diff --git a/runtime/src/main/java/net/laprun/sustainability/power/quarkus/runtime/ServerSampler.java b/runtime/src/main/java/net/laprun/sustainability/power/quarkus/runtime/ServerSampler.java index 30b106c..a1cd0ea 100644 --- a/runtime/src/main/java/net/laprun/sustainability/power/quarkus/runtime/ServerSampler.java +++ b/runtime/src/main/java/net/laprun/sustainability/power/quarkus/runtime/ServerSampler.java @@ -4,7 +4,6 @@ import java.net.URI; import java.time.Duration; import java.util.Arrays; -import java.util.function.BiConsumer; import java.util.function.Consumer; import jakarta.ws.rs.ProcessingException; @@ -33,7 +32,6 @@ public class ServerSampler implements Sampler { private static final long pid = ProcessHandle.current().pid(); private Consumer completed; private Consumer errorHandler; - private BiConsumer sampled; @ConfigProperty(name = "power-server.url", defaultValue = "http://localhost:20432") URI powerServerURI;