Skip to content

Commit b635ca2

Browse files
refactor: separate responsabilities in CarbonEstimator
1 parent 2915590 commit b635ca2

File tree

5 files changed

+83
-29
lines changed

5 files changed

+83
-29
lines changed

domain/src/main/java/fr/ippon/iroco2/domain/calculator/model/Component.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,14 @@
2323
import lombok.Getter;
2424
import lombok.Setter;
2525

26+
import java.time.Duration;
2627
import java.time.LocalDateTime;
2728
import java.util.List;
29+
import java.util.OptionalDouble;
2830
import java.util.UUID;
2931

3032
import static fr.ippon.iroco2.domain.calculator.model.emu.SettingName.MEMORY_IN_MEGA_BYTE;
33+
import static fr.ippon.iroco2.domain.estimator.TimeConstant.MS_IN_ONE_MONTH;
3134
import static java.util.Optional.ofNullable;
3235

3336
@Getter
@@ -52,6 +55,21 @@ public static Component load(UUID id, UUID infrastructureID, String name, LocalD
5255
return new Component(id, infrastructureID, name, lastModificationDate, regionID, service, values);
5356
}
5457

58+
/**
59+
* Calculates the estimated monthly uptime for a component based on its configuration values.
60+
* If no valid configuration values are set, the default uptime ratio is considered as 1.
61+
*
62+
* @return the computed monthly uptime as a {@link Duration}, representing the duration of uptime for one month.
63+
*/
64+
public Duration monthlyUptime() {
65+
var averageUptimeRatio = configurationValues.stream()
66+
.filter(configurationValue -> configurationValue.configurationSettingName().isUptimeParameter())
67+
.map(ConfiguredSetting::averageUptimeRatio)
68+
.flatMapToDouble(OptionalDouble::stream)
69+
.reduce(1., (a, b) -> a * b); // standard product. 1 if no valid configuration is set
70+
return Duration.ofMillis((long) (averageUptimeRatio * MS_IN_ONE_MONTH));
71+
}
72+
5573
public double getMemoryInMegaByte() {
5674
return ofNullable(getValue(MEMORY_IN_MEGA_BYTE))
5775
.map(Double::parseDouble)

domain/src/main/java/fr/ippon/iroco2/domain/calculator/model/ConfiguredSetting.java

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,34 @@
1919

2020
import fr.ippon.iroco2.domain.calculator.model.emu.SettingName;
2121

22+
import java.util.OptionalDouble;
2223
import java.util.UUID;
2324

25+
import static fr.ippon.iroco2.domain.estimator.TimeConstant.AVERAGE_DAYS_PER_MONTH;
26+
import static fr.ippon.iroco2.domain.estimator.TimeConstant.MS_IN_ONE_DAY;
27+
import static fr.ippon.iroco2.domain.estimator.TimeConstant.MS_IN_ONE_MONTH;
28+
import static java.lang.Double.parseDouble;
29+
2430
public record ConfiguredSetting(
2531
UUID configurationSettingId,
2632
SettingName configurationSettingName,
2733
String value) {
34+
35+
public OptionalDouble averageUptimeRatio() {
36+
if (!configurationSettingName.isUptimeParameter()) {
37+
return OptionalDouble.empty();
38+
}
39+
40+
final var value = parseDouble(this.value);
41+
final var averageUptimeRatio = switch (configurationSettingName) {
42+
case INSTANCE_NUMBER, VOLUME_NUMBER, MONTHLY_INVOCATION_COUNT -> value;
43+
case DAYS_ON_PER_MONTH -> value / AVERAGE_DAYS_PER_MONTH;
44+
case DAILY_USAGE_COUNT -> value * AVERAGE_DAYS_PER_MONTH;
45+
case AVERAGE_EXEC_TIME_IN_MS -> value / MS_IN_ONE_MONTH;
46+
case DAILY_RUNNING_TIME_IN_MS -> value / MS_IN_ONE_DAY;
47+
default ->
48+
throw new IllegalStateException("Unexpected configuration setting: '%s'".formatted(configurationSettingName));
49+
};
50+
return OptionalDouble.of(averageUptimeRatio);
51+
}
2852
}

domain/src/main/java/fr/ippon/iroco2/domain/calculator/model/emu/SettingName.java

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,10 @@
1717
*/
1818
package fr.ippon.iroco2.domain.calculator.model.emu;
1919

20+
import java.util.Set;
21+
2022
public enum SettingName {
23+
2124
INSTANCE_NUMBER,
2225
INSTANCE_TYPE,
2326
STORAGE_IN_MEGA_BYTE,
@@ -28,5 +31,11 @@ public enum SettingName {
2831
MEMORY_IN_MEGA_BYTE,
2932
DAILY_USAGE_COUNT,
3033
DAYS_ON_PER_MONTH,
31-
VOLUME_NUMBER
34+
VOLUME_NUMBER;
35+
36+
private static final Set<SettingName> UPTIME_PARAMETERS = Set.of(INSTANCE_NUMBER, VOLUME_NUMBER, MONTHLY_INVOCATION_COUNT, DAYS_ON_PER_MONTH, DAILY_USAGE_COUNT, AVERAGE_EXEC_TIME_IN_MS, DAILY_RUNNING_TIME_IN_MS);
37+
38+
public boolean isUptimeParameter() {
39+
return UPTIME_PARAMETERS.contains(this);
40+
}
3241
}

domain/src/main/java/fr/ippon/iroco2/domain/estimator/CarbonEstimator.java

Lines changed: 2 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818
package fr.ippon.iroco2.domain.estimator;
1919

2020
import fr.ippon.iroco2.domain.calculator.model.Component;
21-
import fr.ippon.iroco2.domain.calculator.model.ConfiguredSetting;
2221
import fr.ippon.iroco2.domain.calculator.model.emu.SettingName;
2322
import fr.ippon.iroco2.domain.commons.DomainService;
2423
import fr.ippon.iroco2.domain.commons.exception.FunctionalException;
@@ -46,12 +45,8 @@
4645
import static fr.ippon.iroco2.domain.calculator.model.emu.SettingName.STORAGE_IN_MEGA_BYTE;
4746
import static fr.ippon.iroco2.domain.commons.model.PayloadConfiguration.INSTANCE_TYPE;
4847
import static fr.ippon.iroco2.domain.commons.model.PayloadConfiguration.S3_STORAGE;
49-
import static fr.ippon.iroco2.domain.estimator.TimeConstant.AVERAGE_DAYS_PER_MONTH;
50-
import static fr.ippon.iroco2.domain.estimator.TimeConstant.MS_IN_ONE_DAY;
51-
import static fr.ippon.iroco2.domain.estimator.TimeConstant.MS_IN_ONE_MONTH;
5248
import static fr.ippon.iroco2.domain.estimator.aws.EC2Instance.TDP_TO_POWER_CONSUMPTION_RATIO;
5349
import static fr.ippon.iroco2.domain.estimator.model.MemoryConfig.ZERO_MB;
54-
import static java.lang.Integer.parseInt;
5550
import static java.math.RoundingMode.UP;
5651

5752
@DomainService
@@ -61,27 +56,6 @@ public class CarbonEstimator {
6156
private final GlobalEnergyMixStorage globalEnergyMixStorage;
6257
private final EC2InstanceStorage ec2InstanceStorage;
6358

64-
private static Duration computeDuration(Component component) {
65-
double percentageUptime = 1;
66-
for (ConfiguredSetting configurationValue : component.getConfigurationValues()) {
67-
switch (configurationValue.configurationSettingName()) {
68-
case INSTANCE_NUMBER, VOLUME_NUMBER, MONTHLY_INVOCATION_COUNT ->
69-
percentageUptime *= parseInt(configurationValue.value());
70-
case DAYS_ON_PER_MONTH ->
71-
percentageUptime *= parseInt(configurationValue.value()) / AVERAGE_DAYS_PER_MONTH;
72-
case DAILY_USAGE_COUNT ->
73-
percentageUptime *= parseInt(configurationValue.value()) * AVERAGE_DAYS_PER_MONTH;
74-
case AVERAGE_EXEC_TIME_IN_MS ->
75-
percentageUptime *= parseInt(configurationValue.value()) / MS_IN_ONE_MONTH;
76-
case DAILY_RUNNING_TIME_IN_MS ->
77-
percentageUptime *= (double) parseInt(configurationValue.value()) / MS_IN_ONE_DAY;
78-
default -> { // not an UpTime parameter
79-
}
80-
}
81-
}
82-
return Duration.ofMillis((long) (percentageUptime * MS_IN_ONE_MONTH));
83-
}
84-
8559
private static MemoryConfig findMemoryConfig(Component component, SettingName settingName) {
8660
return Optional.ofNullable(component.getValue(settingName))
8761
.map(Double::parseDouble)
@@ -124,8 +98,8 @@ public int estimateComponent(String countryIsoCode, Component component) throws
12498
CPUConfig cpu = findCPU(component);
12599
MemoryConfig ram = findMemoryConfig(component, MEMORY_IN_MEGA_BYTE);
126100
MemoryConfig disk = findMemoryConfig(component, STORAGE_IN_MEGA_BYTE);
127-
Duration duration = computeDuration(component);
128-
var estimatableServer = getEstimatableServer(component.getValue(SettingName.INSTANCE_TYPE), disk, duration, cpu, ram);
101+
Duration monthlyUptime = component.monthlyUptime();
102+
var estimatableServer = getEstimatableServer(component.getValue(SettingName.INSTANCE_TYPE), disk, monthlyUptime, cpu, ram);
129103
return estimateServer(countryIsoCode, estimatableServer);
130104
}
131105

domain/src/test/java/fr/ippon/iroco2/domain/calculator/model/ComponentTest.java

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,15 @@
1717
*/
1818
package fr.ippon.iroco2.domain.calculator.model;
1919

20+
import fr.ippon.iroco2.domain.calculator.model.emu.SettingName;
2021
import org.junit.jupiter.api.Test;
2122

23+
import java.time.Duration;
2224
import java.util.List;
2325

2426
import static fr.ippon.iroco2.domain.calculator.model.emu.SettingName.INSTANCE_NUMBER;
27+
import static fr.ippon.iroco2.domain.estimator.TimeConstant.AVERAGE_DAYS_PER_MONTH;
28+
import static fr.ippon.iroco2.domain.estimator.TimeConstant.MS_IN_ONE_MONTH;
2529
import static org.assertj.core.api.Assertions.assertThat;
2630
import static org.mockito.Mockito.mock;
2731

@@ -78,4 +82,29 @@ void getValue_should_return_corresponding_value() {
7882
//then
7983
assertThat(result).isEqualTo("666");
8084
}
85+
86+
@Test
87+
void testMonthlyUptime_WithFilteredValues_ProductCalculation() {
88+
// Given
89+
90+
// 0 = ignored, 2, 3, 5, 7 = kept
91+
var configurationValues = List.of(
92+
new ConfiguredSetting(null, SettingName.PROCESSOR_ARCHITECTURE, "0"),
93+
new ConfiguredSetting(null, SettingName.MEMORY_IN_MEGA_BYTE, "0"),
94+
new ConfiguredSetting(null, SettingName.INSTANCE_NUMBER, "2"),
95+
new ConfiguredSetting(null, SettingName.VOLUME_NUMBER, "3"),
96+
new ConfiguredSetting(null, SettingName.MONTHLY_INVOCATION_COUNT, "5"),
97+
new ConfiguredSetting(null, SettingName.DAYS_ON_PER_MONTH, "7")
98+
);
99+
var component = Component.load(
100+
null, null, "Test", null, null, null, configurationValues
101+
);
102+
103+
// When
104+
Duration uptime = component.monthlyUptime();
105+
106+
// Then
107+
var expectedUptime = Duration.ofMillis((long) (210 * MS_IN_ONE_MONTH / AVERAGE_DAYS_PER_MONTH)); // 2 * 3 * 5 * 7 = 210
108+
assertThat(uptime).isEqualTo(expectedUptime);
109+
}
81110
}

0 commit comments

Comments
 (0)