Skip to content
Snippets Groups Projects
Commit 3d932eca authored by Lorenz Boguhn's avatar Lorenz Boguhn
Browse files

Merge theodolite-kotlin

parents 37e7538d 55b77fdd
No related branches found
No related tags found
4 merge requests!159Re-implementation of Theodolite with Kotlin/Quarkus,!157Update Graal Image in CI pipeline,!116Add image build documentation,!83WIP: Re-implementation of Theodolite with Kotlin/Quarkus
Showing
with 214 additions and 13 deletions
......@@ -8,10 +8,10 @@ import theodolite.util.Results
* The Lower Bound Restriction sets the lower bound of the resources to be examined to the value
* needed to successfully execute the next smaller load.
*
* @param results Result object used as a basis to restrict the resources.
* @param results [Result] object used as a basis to restrict the resources.
*/
class LowerBoundRestriction(results: Results) : RestrictionStrategy(results) {
override fun next(load: LoadDimension, resources: List<Resource>): List<Resource> {
override fun apply(load: LoadDimension, resources: List<Resource>): List<Resource> {
val maxLoad: LoadDimension? = this.results.getMaxBenchmarkedLoad(load)
var lowerBound: Resource? = this.results.getMinRequiredInstances(maxLoad)
if (lowerBound == null) {
......
......@@ -6,18 +6,20 @@ import theodolite.util.Resource
import theodolite.util.Results
/**
* A "Restriction Strategy" restricts a list of resources based on the current
* A 'Restriction Strategy' restricts a list of resources based on the current
* results of all previously performed benchmarks.
*
* @param results the [Results] object
*/
@RegisterForReflection
abstract class RestrictionStrategy(val results: Results) {
/**
* Next Restrict the given resource list for the given load based on the result object.
* Apply the restriction of the given resource list for the given load based on the results object.
*
* @param load Load dimension for which a subset of resources are required.
* @param resources List of resources to be restricted.
* @param load [LoadDimension] for which a subset of resources are required.
* @param resources List of [Resource]s to be restricted.
* @return Returns a list containing only elements that have not been filtered out by the
* restriction (possibly empty).
*/
abstract fun next(load: LoadDimension, resources: List<Resource>): List<Resource>
abstract fun apply(load: LoadDimension, resources: List<Resource>): List<Resource>
}
......@@ -5,7 +5,7 @@ import theodolite.util.LoadDimension
import theodolite.util.Resource
/**
* Search for the smallest suitable resource with binary search.
* Binary-search-like implementation for determining the smallest suitable number of instances.
*
* @param benchmarkExecutor Benchmark executor which runs the individual benchmarks.
*/
......@@ -18,6 +18,14 @@ class BinarySearch(benchmarkExecutor: BenchmarkExecutor) : SearchStrategy(benchm
return resources[result]
}
/**
* Apply binary search.
*
* @param load the load dimension to perform experiments for
* @param resources the list in which binary search is performed
* @param lower lower bound for binary search (inclusive)
* @param upper upper bound for binary search (inclusive)
*/
private fun binarySearch(load: LoadDimension, resources: List<Resource>, lower: Int, upper: Int): Int {
if (lower > upper) {
throw IllegalArgumentException()
......
......@@ -6,6 +6,13 @@ import theodolite.strategies.restriction.RestrictionStrategy
import theodolite.util.LoadDimension
import theodolite.util.Resource
/**
* Composite strategy that combines a SearchStrategy and a set of RestrictionStrategy.
*
* @param searchStrategy the [SearchStrategy] that is executed as part of this [CompositeStrategy].
* @param restrictionStrategies the set of [RestrictionStrategy] that are connected conjuntively to restrict the [Resource]
* @param benchmarkExecutor Benchmark executor which runs the individual benchmarks.
*/
@RegisterForReflection
class CompositeStrategy(
benchmarkExecutor: BenchmarkExecutor,
......@@ -16,7 +23,7 @@ class CompositeStrategy(
override fun findSuitableResource(load: LoadDimension, resources: List<Resource>): Resource? {
var restrictedResources = resources.toList()
for (strategy in this.restrictionStrategies) {
restrictedResources = restrictedResources.intersect(strategy.next(load, resources)).toList()
restrictedResources = restrictedResources.intersect(strategy.apply(load, resources)).toList()
}
return this.searchStrategy.findSuitableResource(load, restrictedResources)
}
......
......@@ -4,6 +4,11 @@ import theodolite.execution.BenchmarkExecutor
import theodolite.util.LoadDimension
import theodolite.util.Resource
/**
* Linear-search-like implementation for determining the smallest suitable number of instances.
*
* @param benchmarkExecutor Benchmark executor which runs the individual benchmarks.
*/
class LinearSearch(benchmarkExecutor: BenchmarkExecutor) : SearchStrategy(benchmarkExecutor) {
override fun findSuitableResource(load: LoadDimension, resources: List<Resource>): Resource? {
......
......@@ -5,13 +5,19 @@ import theodolite.execution.BenchmarkExecutor
import theodolite.util.LoadDimension
import theodolite.util.Resource
/**
* Base class for the implementation for SearchStrategies. SearchStrategies determine the smallest suitable number of instances.
*
* @param benchmarkExecutor Benchmark executor which runs the individual benchmarks.
*/
@RegisterForReflection
abstract class SearchStrategy(val benchmarkExecutor: BenchmarkExecutor) {
/**
* Find smallest suitable resource from the specified resource list for the given load.
*
* @param load Load to be tested.
* @param resources List of all possible resources.
* @param load the [LoadDimension] to be tested.
* @param resources List of all possible [Resource]s.
*
* @return suitable resource for the specified load, or null if no suitable resource exists.
*/
abstract fun findSuitableResource(load: LoadDimension, resources: List<Resource>): Resource?
......
......@@ -3,6 +3,13 @@ package theodolite.util
import io.quarkus.runtime.annotations.RegisterForReflection
import theodolite.strategies.searchstrategy.CompositeStrategy
/**
* Config class that represents a configuration of a theodolite run.
*
* @param loads the [LoadDimension] of the execution
* @param resources the [Resource] of the execution
* @param compositeStrategy the [CompositeStrategy] of the execution
*/
@RegisterForReflection
data class Config(
val loads: List<LoadDimension>,
......
......@@ -3,9 +3,19 @@ package theodolite.util
import com.fasterxml.jackson.databind.annotation.JsonDeserialize
import io.quarkus.runtime.annotations.RegisterForReflection
/**
* Representation of a configuration override.
*/
@JsonDeserialize
@RegisterForReflection
class ConfigurationOverride {
/**
* Patcher of the configuration override.
*/
lateinit var patcher: PatcherDefinition
/**
* Value of the patched configuration override.
*/
lateinit var value: String
}
......@@ -5,20 +5,51 @@ import io.quarkus.runtime.annotations.RegisterForReflection
import org.apache.kafka.clients.admin.NewTopic
import kotlin.properties.Delegates
/**
* Configuration of Kafka connection.
*
* @see TopicWrapper
*/
@RegisterForReflection
@JsonDeserialize
class KafkaConfig {
/**
* The bootstrap server connection string
*/
lateinit var bootstrapServer: String
/**
* The list of topics
*/
lateinit var topics: List<TopicWrapper>
/**
* Get all current Kafka topics.
*
* @return the list of topics.
*/
fun getKafkaTopics(): List<NewTopic> {
return topics.map { topic -> NewTopic(topic.name, topic.numPartitions, topic.replicationFactor) }
}
/**
* Wrapper for a topic definition.
*/
@RegisterForReflection
class TopicWrapper {
/**
* The topic name
*/
lateinit var name: String
/**
* The number of partitions
*/
var numPartitions by Delegates.notNull<Int>()
/**
* The replication factor of this topic
*/
var replicationFactor by Delegates.notNull<Short>()
}
}
......@@ -2,13 +2,24 @@ package theodolite.util
import io.quarkus.runtime.annotations.RegisterForReflection
/**
* Representation of the load dimensions for a execution of theodolite.
*
* @param number the value of this [LoadDimension]
* @param type [PatcherDefinition] of this [LoadDimension]
*/
@RegisterForReflection
data class LoadDimension(private val number: Int, private val type: List<PatcherDefinition>) {
/**
* @return the value of this load dimension.
*/
fun get(): Int {
return this.number
}
/**
* @return the list of [PatcherDefinition]
*/
fun getType(): List<PatcherDefinition> {
return this.type
}
......
package theodolite.util
/**
* Interface for parsers.
* A parser allows the reading of files and creates a corresponding object from them.
*/
interface Parser {
/**
* Parse a file.
*
* @param path The path of the file
* @param E The class of the type to parse
* @param T The type to parse
*/
fun <T> parse(path: String, E: Class<T>): T?
}
......@@ -3,11 +3,29 @@ package theodolite.util
import com.fasterxml.jackson.databind.annotation.JsonDeserialize
import io.quarkus.runtime.annotations.RegisterForReflection
/**
* Definition of the structure of a [theodolite.patcher.AbstractPatcher] which implements the [theodolite.patcher.Patcher] interface.
*/
@JsonDeserialize
@RegisterForReflection
class PatcherDefinition {
/**
* The type of the patcher
*/
lateinit var type: String
/**
* The resource which the patcher is applied to
*/
lateinit var resource: String
/**
* The container which the patcher is applied to
*/
lateinit var container: String
/**
* The variable name for the patcher
*/
lateinit var variableName: String
}
......@@ -2,25 +2,58 @@ package theodolite.util
import io.quarkus.runtime.annotations.RegisterForReflection
/**
* This class corresponds to the JSON response format of a Prometheus
* [range-query](https://www.prometheus.io/docs/prometheus/latest/querying/api/#range-queries)
*/
@RegisterForReflection
data class PrometheusResponse(
/**
* Indicates whether the query was successful.
*/
var status: String? = null,
/**
* The data section of the query result contains the information about the resultType and the values itself.
*/
var data: PromData? = null
)
/**
* Description of Prometheus data.
*
* Based on [PromResult]
*/
@RegisterForReflection
data class PromData(
/**
* Type of the result, either "matrix" | "vector" | "scalar" | "string"
*/
var resultType: String? = null,
/**
* Result of the range-query. In the case of range-query this corresponds to the [range-vectors result format](https://www.prometheus.io/docs/prometheus/latest/querying/api/#range-vectors)
*/
var result: List<PromResult>? = null
)
/**
* PromResult corresponds to the [range-vectors result format](https://www.prometheus.io/docs/prometheus/latest/querying/api/#range-vectors)
*/
@RegisterForReflection
data class PromResult(
/**
* Label of the metric
*/
var metric: PromMetric? = null,
/**
* Values of the metric (e.g. [ [ <unix_time>, "<sample_value>" ], ... ])
*/
var values: List<Any>? = null
)
/**
* Corresponds to the metric field in the range-vector result format of a Prometheus range-query response.
*/
@RegisterForReflection
data class PromMetric(
var group: String? = null
)
)
\ No newline at end of file
......@@ -2,13 +2,22 @@ package theodolite.util
import io.quarkus.runtime.annotations.RegisterForReflection
/**
* Representation of the resources for an execution of Theodolite.
*/
@RegisterForReflection
data class Resource(private val number: Int, private val type: List<PatcherDefinition>) {
/**
* @return the value of this resource.
*/
fun get(): Int {
return this.number
}
/**
* @return the list of [PatcherDefinition]
*/
fun getType(): List<PatcherDefinition> {
return this.type
}
......
......@@ -2,18 +2,47 @@ package theodolite.util
import io.quarkus.runtime.annotations.RegisterForReflection
/**
* Central class that saves the state of a execution of Theodolite. For an execution, it is used to save the result of
* individual experiments. Further, it is used by the RestrictionStrategy to
* perform the [theodolite.strategies.restriction.RestrictionStrategy].
*/
@RegisterForReflection
class Results {
private val results: MutableMap<Pair<LoadDimension, Resource>, Boolean> = mutableMapOf()
/**
* Set the result for an experiment.
*
* @param experiment A pair that identifies the experiment by the [LoadDimension] and [Resource].
* @param successful the result of the experiment. Successful == true and Unsuccessful == false.
*/
fun setResult(experiment: Pair<LoadDimension, Resource>, successful: Boolean) {
this.results[experiment] = successful
}
/**
* Get the result for an experiment.
*
* @param experiment A pair that identifies the experiment by the [LoadDimension] and [Resource].
* @return true if the experiment was successful and false otherwise. If the result has not been reported so far,
* null is returned.
*
* @see Resource
*/
fun getResult(experiment: Pair<LoadDimension, Resource>): Boolean? {
return this.results[experiment]
}
/**
* Get the smallest suitable number of instances for a specified [LoadDimension].
*
* @param load the [LoadDimension]
*
* @return the smallest suitable number of resources. If the experiment was not executed yet,
* a @see Resource with the constant Int.MAX_VALUE as value is returned. If no experiments have been marked as either successful or unsuccessful
* yet, a Resource with the constant value Int.MIN_VALUE is returned.
*/
fun getMinRequiredInstances(load: LoadDimension?): Resource? {
if (this.results.isEmpty()) return Resource(Int.MIN_VALUE, emptyList())
......@@ -30,6 +59,14 @@ class Results {
return requiredInstances
}
/**
* Get the largest [LoadDimension] that has been reported executed successfully (or unsuccessfully) so far, for a
* [LoadDimension] and is smaller than the given [LoadDimension].
*
* @param load the [LoadDimension]
*
* @return the largest [LoadDimension] or null, if there is none for this [LoadDimension]
*/
fun getMaxBenchmarkedLoad(load: LoadDimension): LoadDimension? {
var maxBenchmarkedLoad: LoadDimension? = null
for (experiment in results) {
......
......@@ -3,6 +3,9 @@ package theodolite.util
import com.fasterxml.jackson.databind.annotation.JsonDeserialize
import io.quarkus.runtime.annotations.RegisterForReflection
/**
* The TypeName encapsulates a list of [PatcherDefinition] along with a typeName that specifies for what the [PatcherDefinition] should be used.
*/
@RegisterForReflection
@JsonDeserialize
class TypeName {
......
......@@ -6,6 +6,9 @@ import java.io.File
import java.io.FileInputStream
import java.io.InputStream
/**
* The YamlParser parses a YAML file
*/
class YamlParser : Parser {
override fun <T> parse(path: String, E: Class<T>): T? {
val input: InputStream = FileInputStream(File(path))
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment