Skip to content

Commit

Permalink
Benchmark feature implementation (#34)
Browse files Browse the repository at this point in the history
  • Loading branch information
Tan108 authored Apr 10, 2024
1 parent d802652 commit 1c2139d
Show file tree
Hide file tree
Showing 2 changed files with 183 additions and 20 deletions.
149 changes: 149 additions & 0 deletions src/main/kotlin/com/featurevisor/testRunner/BenchmarkFeature.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
package com.featurevisor.testRunner

import com.featurevisor.sdk.FeaturevisorInstance
import com.featurevisor.sdk.getVariable
import com.featurevisor.sdk.getVariation
import com.featurevisor.sdk.isEnabled
import com.featurevisor.types.*

data class BenchmarkOutput(
val value: Any? = null,
val duration: Double
)

data class BenchMarkOptions(
val environment: String = "",
val feature: String = "",
val n: Int = 0,
val projectRootPath: String = "",
val context: Context = emptyMap(),
val variation: Boolean? = null,
val variable: String? = null,
)

fun benchmarkFeature(option: BenchMarkOptions) {
println("Running benchmark for feature ${option.feature}...")

println("Building datafile containing all features for ${option.environment}...")

val datafileBuildStart = System.nanoTime().toDouble()

val datafileContent = buildDataFileForStaging(option.projectRootPath)

val datafileBuildEnd = System.nanoTime().toDouble()

val datafileBuildDuration = datafileBuildEnd - datafileBuildStart

println("Datafile build duration: ${convertNanoSecondToMilliSecond(datafileBuildDuration)}")

val sdk = initializeSdkWithDataFileContent(datafileContent)

println("...SDK initialized")

println("Against context: ${option.context}")

val output: BenchmarkOutput

if (option.variable != null) {
println("Evaluating variable ${option.variable} ${option.n} times...")

output = benchmarkFeatureVariable(
sdk,
feature = option.feature,
variableKey = option.variable,
context = option.context,
n = option.n
)

} else if (option.variation != null) {
println("Evaluating variation ${option.variation} ${option.n} times...")

output = benchmarkFeatureVariation(
sdk,
feature = option.feature,
context = option.context,
n = option.n
)
} else {
println("Evaluating flag ${option.n} times...")

output = benchmarkFeatureFlag(
sdk,
feature = option.feature,
context = option.context,
n = option.n
)
}

println("Evaluated value : ${output.value}")
println("Total duration : ${convertNanoSecondToMilliSecond(output.duration)}")
if (option.n != 0) {
println("Average duration: ${convertNanoSecondToMilliSecond(output.duration / option.n)}")
}
}


fun benchmarkFeatureFlag(
f: FeaturevisorInstance,
feature: FeatureKey,
context: Context,
n: Int
): BenchmarkOutput {
val start = System.nanoTime().toDouble()
var value: Any = false

for (i in 0..n) {
value = f.isEnabled(featureKey = feature, context = context)
}

val end = System.nanoTime().toDouble()

return BenchmarkOutput(
value = value,
duration = end - start
)
}


fun benchmarkFeatureVariation(
f: FeaturevisorInstance,
feature: FeatureKey,
context: Context,
n: Int
): BenchmarkOutput {
val start = System.nanoTime().toDouble()
var value: VariationValue? = null

for (i in 0..n) {
value = f.getVariation(featureKey = feature, context = context)
}

val end = System.nanoTime().toDouble()

return BenchmarkOutput(
value = value,
duration = end - start
)
}

fun benchmarkFeatureVariable(
f: FeaturevisorInstance,
feature: FeatureKey,
variableKey: VariableKey,
context: Context,
n: Int
): BenchmarkOutput {
val start = System.nanoTime().toDouble()
var value: VariableValue? = null

for (i in 0..n) {
value = f.getVariable(featureKey = feature, variableKey = variableKey, context = context)
}

val end = System.nanoTime().toDouble()

return BenchmarkOutput(
value = value,
duration = end - start
)
}
54 changes: 34 additions & 20 deletions src/main/kotlin/com/featurevisor/testRunner/Utils.kt
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,13 @@ internal fun getSdkInstance(datafileContent: DatafileContent?, assertion: Featur
)
)

internal fun initializeSdkWithDataFileContent(datafileContent: DatafileContent?) =
FeaturevisorInstance.createInstance(
InstanceOptions(
datafile = datafileContent,
)
)

internal fun getFileForSpecificPath(path: String) = File(path)

internal inline fun <reified R : Any> String.convertToDataClass() = json.decodeFromString<R>(this)
Expand Down Expand Up @@ -230,30 +237,29 @@ fun checkJsonIsEquals(a: String, b: String): Boolean {
return map1 == map2
}

fun buildDataFileForBothEnvironments(projectRootPath: String): DataFile {
val dataFileForStaging = try {
getJsonForDataFile(environment = "staging", projectRootPath = projectRootPath)?.run {
convertToDataClass<DatafileContent>()
}
} catch (e: Exception) {
printMessageInRedColor("Unable to parse staging data file")
null
}
fun buildDataFileForBothEnvironments(projectRootPath: String): DataFile =
DataFile(
stagingDataFiles = buildDataFileForStaging(projectRootPath),
productionDataFiles = buildDataFileForProduction(projectRootPath)
)

val dataFileForProduction = try {
getJsonForDataFile(environment = "production", projectRootPath = projectRootPath)?.run {
convertToDataClass<DatafileContent>()
}
fun buildDataFileForStaging(projectRootPath: String) = try {
getJsonForDataFile(environment = "staging", projectRootPath = projectRootPath)?.run {
convertToDataClass<DatafileContent>()
}
} catch (e: Exception) {
printMessageInRedColor("Unable to parse staging data file")
null
}

} catch (e: Exception) {
printMessageInRedColor("Unable to parse production data file")
null
fun buildDataFileForProduction(projectRootPath: String) = try {
getJsonForDataFile(environment = "production", projectRootPath = projectRootPath)?.run {
convertToDataClass<DatafileContent>()
}

return DataFile(
stagingDataFiles = dataFileForStaging,
productionDataFiles = dataFileForProduction
)
} catch (e: Exception) {
printMessageInRedColor("Unable to parse production data file")
null
}

fun getDataFileContent(featureName: String, environment: String, projectRootPath: String) =
Expand All @@ -270,4 +276,12 @@ fun getDataFileContent(featureName: String, environment: String, projectRootPath
null
}

fun convertNanoSecondToMilliSecond(timeInNanoSecond:Double):String {
val timeInMilliSecond = timeInNanoSecond/1000000
return if (timeInMilliSecond > 1000){
"${timeInMilliSecond / 1000} s"
}else{
"$timeInMilliSecond ms"
}
}

0 comments on commit 1c2139d

Please sign in to comment.