Skip to content
Snippets Groups Projects
Commit 4c846e8c authored by Sören Henning's avatar Sören Henning
Browse files

Merge branch 'floats-in-slo' into 'master'

Support floating point values in SLO evaluation

Closes #385

See merge request !286
parents f292cac0 e542ff07
No related branches found
No related tags found
1 merge request!286Support floating point values in SLO evaluation
Pipeline #8645 passed
...@@ -37,7 +37,7 @@ def aggr_query(values: dict, warmup: int, aggr_func): ...@@ -37,7 +37,7 @@ def aggr_query(values: dict, warmup: int, aggr_func):
df = pd.DataFrame.from_dict(values) df = pd.DataFrame.from_dict(values)
df.columns = ['timestamp', 'value'] df.columns = ['timestamp', 'value']
filtered = df[df['timestamp'] >= (df['timestamp'][0] + warmup)] filtered = df[df['timestamp'] >= (df['timestamp'][0] + warmup)]
filtered['value'] = filtered['value'].astype(float).astype(int) filtered['value'] = filtered['value'].astype(float)
return filtered['value'].aggregate(aggr_func) return filtered['value'].aggregate(aggr_func)
def check_result(result, operator: str, threshold): def check_result(result, operator: str, threshold):
...@@ -63,7 +63,7 @@ async def check_slo(request: Request): ...@@ -63,7 +63,7 @@ async def check_slo(request: Request):
query_aggregation = get_aggr_func(data['metadata']['queryAggregation']) query_aggregation = get_aggr_func(data['metadata']['queryAggregation'])
rep_aggregation = get_aggr_func(data['metadata']['repetitionAggregation']) rep_aggregation = get_aggr_func(data['metadata']['repetitionAggregation'])
operator = data['metadata']['operator'] operator = data['metadata']['operator']
threshold = int(data['metadata']['threshold']) threshold = float(data['metadata']['threshold'])
query_results = [aggr_query(r[0]["values"], warmup, query_aggregation) for r in data["results"]] query_results = [aggr_query(r[0]["values"], warmup, query_aggregation) for r in data["results"]]
result = pd.DataFrame(query_results).aggregate(rep_aggregation).at[0] result = pd.DataFrame(query_results).aggregate(rep_aggregation).at[0]
......
...@@ -27,7 +27,7 @@ def calculate_slope_trend(results, warmup): ...@@ -27,7 +27,7 @@ def calculate_slope_trend(results, warmup):
group = result['metric'].get('consumergroup', "default") group = result['metric'].get('consumergroup', "default")
for value in result['values']: for value in result['values']:
d.append({'group': group, 'timestamp': int( d.append({'group': group, 'timestamp': int(
value[0]), 'value': int(value[1]) if value[1] != 'NaN' else 0}) value[0]), 'value': float(value[1]) if value[1] != 'NaN' else 0})
df = pd.DataFrame(d) df = pd.DataFrame(d)
......
...@@ -11,8 +11,8 @@ import java.net.ConnectException ...@@ -11,8 +11,8 @@ import java.net.ConnectException
* @param metadata metadata passed to the external SLO checker. * @param metadata metadata passed to the external SLO checker.
*/ */
class ExternalSloChecker( class ExternalSloChecker(
private val externalSlopeURL: String, val externalSlopeURL: String,
private val metadata: Map<String, Any> val metadata: Map<String, Any>
) : SloChecker { ) : SloChecker {
private val RETRIES = 2 private val RETRIES = 2
......
...@@ -41,7 +41,7 @@ class SloCheckerFactory { ...@@ -41,7 +41,7 @@ class SloCheckerFactory {
*/ */
fun create( fun create(
sloType: String, sloType: String,
properties: MutableMap<String, String>, properties: Map<String, String>,
load: Int, load: Int,
resource: Int, resource: Int,
metric: Metric metric: Metric
...@@ -58,7 +58,7 @@ class SloCheckerFactory { ...@@ -58,7 +58,7 @@ class SloCheckerFactory {
"repetitionAggregation" to (properties["repetitionAggregation"] "repetitionAggregation" to (properties["repetitionAggregation"]
?: throw IllegalArgumentException("repetitionAggregation expected")), ?: throw IllegalArgumentException("repetitionAggregation expected")),
"operator" to (properties["operator"] ?: throw IllegalArgumentException("operator expected")), "operator" to (properties["operator"] ?: throw IllegalArgumentException("operator expected")),
"threshold" to (properties["threshold"]?.toInt() "threshold" to (properties["threshold"]?.toDouble()
?: throw IllegalArgumentException("threshold expected")) ?: throw IllegalArgumentException("threshold expected"))
) )
) )
...@@ -67,7 +67,7 @@ class SloCheckerFactory { ...@@ -67,7 +67,7 @@ class SloCheckerFactory {
?: throw IllegalArgumentException("externalSloUrl expected"), ?: throw IllegalArgumentException("externalSloUrl expected"),
metadata = mapOf( metadata = mapOf(
"warmup" to (properties["warmup"]?.toInt() ?: throw IllegalArgumentException("warmup expected")), "warmup" to (properties["warmup"]?.toInt() ?: throw IllegalArgumentException("warmup expected")),
"threshold" to (properties["threshold"]?.toInt() "threshold" to (properties["threshold"]?.toDouble()
?: throw IllegalArgumentException("threshold expected")) ?: throw IllegalArgumentException("threshold expected"))
) )
) )
...@@ -78,8 +78,8 @@ class SloCheckerFactory { ...@@ -78,8 +78,8 @@ class SloCheckerFactory {
if (thresholdRatio < 0.0) { if (thresholdRatio < 0.0) {
throw IllegalArgumentException("Threshold ratio needs to be an Double greater or equal 0.0") throw IllegalArgumentException("Threshold ratio needs to be an Double greater or equal 0.0")
} }
// cast to int, as rounding is not really necessary
val threshold = (load * thresholdRatio).toInt() val threshold = (load * thresholdRatio)
ExternalSloChecker( ExternalSloChecker(
externalSlopeURL = properties["externalSloUrl"] externalSlopeURL = properties["externalSloUrl"]
......
...@@ -23,18 +23,15 @@ internal class ExternalSloCheckerTest { ...@@ -23,18 +23,15 @@ internal class ExternalSloCheckerTest {
@AfterEach @AfterEach
fun stop() { fun stop() {
wireMockServer?.let { wireMockServer?.stop()
it.stop()
}
} }
@Test @Test
fun testExternalTrueResult() { fun testExternalTrueResult() {
stubFor( this.wireMockServer!!.stubFor(
post(urlEqualTo("/")) post(urlEqualTo("/"))
.willReturn( .willReturn(
aResponse() aResponse().withJsonBody(BooleanNode.getTrue())
.withJsonBody(BooleanNode.getTrue())
) )
) )
...@@ -48,11 +45,10 @@ internal class ExternalSloCheckerTest { ...@@ -48,11 +45,10 @@ internal class ExternalSloCheckerTest {
@Test @Test
fun testExternalFalseResult() { fun testExternalFalseResult() {
stubFor( this.wireMockServer!!.stubFor(
post(urlEqualTo("/")) post(urlEqualTo("/"))
.willReturn( .willReturn(
aResponse() aResponse().withJsonBody(BooleanNode.getFalse())
.withJsonBody(BooleanNode.getFalse())
) )
) )
......
package rocks.theodolite.kubernetes.slo
import io.quarkus.test.junit.QuarkusTest
import org.junit.jupiter.api.Assertions.*
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows
import rocks.theodolite.core.strategies.Metric
@QuarkusTest
internal class SloCheckerFactoryTest {
@Test
fun testCreateGenericSloWithoutUrl() {
val factory = SloCheckerFactory()
assertThrows<IllegalArgumentException> {
factory.create(
SloTypes.GENERIC.value,
mapOf(
"warmup" to "60",
"queryAggregation" to "median",
"repetitionAggregation" to "median",
"operator" to "lte",
"threshold" to "1234"
),
100,
5,
Metric.DEMAND
)
}
}
@Test
fun testCreateGenericSloWithoutWarmup() {
val factory = SloCheckerFactory()
assertThrows<IllegalArgumentException> {
factory.create(
SloTypes.GENERIC.value,
mapOf(
"externalSloUrl" to "http://localhost:1234",
"queryAggregation" to "median",
"repetitionAggregation" to "median",
"operator" to "lte",
"threshold" to "1234"
),
100,
5,
Metric.DEMAND
)
}
}
@Test
fun testCreateGenericSloWithoutQueryAggregation() {
val factory = SloCheckerFactory()
assertThrows<IllegalArgumentException> {
factory.create(
SloTypes.GENERIC.value,
mapOf(
"externalSloUrl" to "http://localhost:1234",
"warmup" to "60",
"repetitionAggregation" to "median",
"operator" to "lte",
"threshold" to "1234"
),
100,
5,
Metric.DEMAND
)
}
}
@Test
fun testCreateGenericSloWithoutRepetitionAggregation() {
val factory = SloCheckerFactory()
assertThrows<IllegalArgumentException> {
factory.create(
SloTypes.GENERIC.value,
mapOf(
"externalSloUrl" to "http://localhost:1234",
"warmup" to "60",
"queryAggregation" to "median",
"operator" to "lte",
"threshold" to "1234"
),
100,
5,
Metric.DEMAND
)
}
}
@Test
fun testCreateGenericSloWithoutOperator() {
val factory = SloCheckerFactory()
assertThrows<IllegalArgumentException> {
factory.create(
SloTypes.GENERIC.value,
mapOf(
"externalSloUrl" to "http://localhost:1234",
"warmup" to "60",
"queryAggregation" to "median",
"repetitionAggregation" to "median",
"threshold" to "1234"
),
100,
5,
Metric.DEMAND
)
}
}
@Test
fun testCreateGenericSloWithoutThreshold() {
val factory = SloCheckerFactory()
assertThrows<IllegalArgumentException> {
factory.create(
SloTypes.GENERIC.value,
mapOf(
"externalSloUrl" to "http://localhost:1234",
"warmup" to "60",
"queryAggregation" to "median",
"repetitionAggregation" to "median",
"operator" to "lte",
),
100,
5,
Metric.DEMAND
)
}
}
@Test
fun testCreateGenericSloFloatThreshold() {
val factory = SloCheckerFactory()
val sloChecker = factory.create(
SloTypes.GENERIC.value,
mapOf(
"externalSloUrl" to "http://localhost:1234",
"warmup" to "60",
"queryAggregation" to "median",
"repetitionAggregation" to "median",
"operator" to "lte",
"threshold" to "12.34"
),
100,
5,
Metric.DEMAND
)
assertInstanceOf(ExternalSloChecker::class.java, sloChecker)
val threshold = (sloChecker as ExternalSloChecker).metadata["threshold"]
assertTrue(threshold is Double, "Expected threshold to be Double.")
assertEquals(12.34, threshold as Double, 0.01)
}
@Test
fun testCreateLagTrendSloWithoutUrl() {
val factory = SloCheckerFactory()
assertThrows<IllegalArgumentException> {
factory.create(
SloTypes.LAG_TREND.value,
mapOf(
"warmup" to "60",
"threshold" to "1234"
),
100,
5,
Metric.DEMAND
)
}
}
@Test
fun testCreateLagTrendSloWithoutWarmup() {
val factory = SloCheckerFactory()
assertThrows<IllegalArgumentException> {
factory.create(
SloTypes.LAG_TREND.value,
mapOf(
"externalSloUrl" to "http://localhost:1234",
"threshold" to "1234"
),
100,
5,
Metric.DEMAND
)
}
}
@Test
fun testCreateLagTrendSloWithoutThreshold() {
val factory = SloCheckerFactory()
assertThrows<IllegalArgumentException> {
factory.create(
SloTypes.LAG_TREND.value,
mapOf(
"externalSloUrl" to "http://localhost:1234",
"warmup" to "60",
),
100,
5,
Metric.DEMAND
)
}
}
@Test
fun testCreateLagTrendSloFloatThreshold() {
val factory = SloCheckerFactory()
val sloChecker = factory.create(
SloTypes.LAG_TREND.value,
mapOf(
"externalSloUrl" to "http://localhost:1234",
"warmup" to "60",
"threshold" to "12.34"
),
100,
5,
Metric.DEMAND
)
assertInstanceOf(ExternalSloChecker::class.java, sloChecker)
val threshold = (sloChecker as ExternalSloChecker).metadata["threshold"]
assertTrue(threshold is Double, "Expected threshold to be Double.")
assertEquals(12.34, threshold as Double, 0.01)
}
@Test
fun testCreateLagTrendRatioSloWithoutUrl() {
val factory = SloCheckerFactory()
assertThrows<IllegalArgumentException> {
factory.create(
SloTypes.LAG_TREND_RATIO.value,
mapOf(
"warmup" to "60",
"ratio" to "0.123"
),
100,
5,
Metric.DEMAND
)
}
}
@Test
fun testCreateLagTrendRatioSloWithoutWarmup() {
val factory = SloCheckerFactory()
assertThrows<IllegalArgumentException> {
factory.create(
SloTypes.LAG_TREND_RATIO.value,
mapOf(
"externalSloUrl" to "http://localhost:1234",
"ratio" to "0.123"
),
100,
5,
Metric.DEMAND
)
}
}
@Test
fun testCreateLagTrendRatioSloWithoutRatioThreshold() {
val factory = SloCheckerFactory()
assertThrows<IllegalArgumentException> {
factory.create(
SloTypes.LAG_TREND_RATIO.value,
mapOf(
"externalSloUrl" to "http://localhost:1234",
"warmup" to "60",
),
100,
5,
Metric.DEMAND
)
}
}
@Test
fun testCreateLagTrendRatioSloFloatThreshold() {
val factory = SloCheckerFactory()
val sloChecker = factory.create(
SloTypes.LAG_TREND_RATIO.value,
mapOf(
"externalSloUrl" to "http://localhost:1234",
"warmup" to "60",
"ratio" to "0.123"
),
100,
5,
Metric.DEMAND
)
assertInstanceOf(ExternalSloChecker::class.java, sloChecker)
val threshold = (sloChecker as ExternalSloChecker).metadata["threshold"]
assertTrue(threshold is Double, "Expected threshold to be Double.")
assertEquals(12.3, threshold as Double, 0.01)
}
}
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please to comment