Skip to content

Commit

Permalink
update evaluation package, LoadStoreCache
Browse files Browse the repository at this point in the history
  • Loading branch information
Tim Yiu authored and Tim Yiu committed Sep 22, 2023
1 parent 58be549 commit 606c306
Show file tree
Hide file tree
Showing 20 changed files with 51 additions and 110 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package com.amplitude.experiment.evaluation
import kotlinx.serialization.Serializable

@Serializable
data class EvaluationAllocation(
internal data class EvaluationAllocation(
val range: List<Int>,
val distributions: List<EvaluationDistribution>,
)
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package com.amplitude.experiment.evaluation
import kotlinx.serialization.Serializable

@Serializable
data class EvaluationBucket(
internal data class EvaluationBucket(
// How to select the prop from the context.
val selector: List<String>,

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package com.amplitude.experiment.evaluation
import kotlinx.serialization.Serializable

@Serializable
data class EvaluationCondition(
internal data class EvaluationCondition(
// How to select the property from the evaluation state.
val selector: List<String>,

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package com.amplitude.experiment.evaluation
import kotlinx.serialization.Serializable

@Serializable
class EvaluationContext : MutableMap<String, Any?> by LinkedHashMap(), Selectable {
internal class EvaluationContext : MutableMap<String, Any?> by LinkedHashMap(), Selectable {

override fun select(selector: String): Any? = this[selector]
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package com.amplitude.experiment.evaluation
import kotlinx.serialization.Serializable

@Serializable
data class EvaluationDistribution(
internal data class EvaluationDistribution(
// The key of the variant to deliver if this range matches.
val variant: String,

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@ import kotlinx.serialization.decodeFromString
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.JsonArray

interface EvaluationEngine {
internal interface EvaluationEngine {
fun evaluate(
context: EvaluationContext,
flags: List<EvaluationFlag>
): Map<String, EvaluationVariant>
}

class EvaluationEngineImpl(private val log: Logger? = DefaultLogger()) : EvaluationEngine {
internal class EvaluationEngineImpl(private val log: Logger? = DefaultLogger()) : EvaluationEngine {

data class EvaluationTarget(
val context: EvaluationContext,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import kotlinx.serialization.Serializable
import kotlinx.serialization.UseSerializers

@Serializable
data class EvaluationFlag(
internal data class EvaluationFlag(
// The flag key. Must be unique for deployment.
val key: String,

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package com.amplitude.experiment.evaluation

object EvaluationOperator {
internal object EvaluationOperator {
const val IS = "is"
const val IS_NOT = "is not"
const val CONTAINS = "contains"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import kotlinx.serialization.Serializable
import kotlinx.serialization.UseSerializers

@Serializable
data class EvaluationSegment(
internal data class EvaluationSegment(
// How to bucket the user given a matching condition.
val bucket: EvaluationBucket? = null,

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import kotlinx.serialization.Serializable
import kotlinx.serialization.UseSerializers

@Serializable
data class EvaluationVariant(
internal data class EvaluationVariant(
val key: String,
val value: Any? = null,
val payload: Any? = null,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
package com.amplitude.experiment.evaluation

enum class Level {
internal enum class Level {
VERBOSE,
DEBUG,
INFO,
WARN,
ERROR
}

interface Logger {
internal interface Logger {
fun verbose(log: () -> String)
fun debug(log: () -> String)
fun info(log: () -> String)
fun warn(e: Throwable? = null, log: () -> String)
fun error(e: Throwable? = null, log: () -> String)
}

class DefaultLogger(
internal class DefaultLogger(
private val level: Level = Level.ERROR,
private val tag: String = "Experiment"
) : Logger {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
package com.amplitude.experiment.evaluation

class CycleException(val cycle: Set<String>) : RuntimeException() {
internal class CycleException(val cycle: Set<String>) : RuntimeException() {
override val message: String
get() = "Detected a cycle between flags $cycle"
}

@Throws(CycleException::class)
fun topologicalSort(
internal fun topologicalSort(
flagConfigs: List<EvaluationFlag>,
flagKeys: Set<String> = setOf()
): List<EvaluationFlag> {
return topologicalSort(flagConfigs.associateBy { it.key }, flagKeys)
}

@Throws(CycleException::class)
fun topologicalSort(
internal fun topologicalSort(
flagConfigs: Map<String, EvaluationFlag>,
flagKeys: Set<String> = setOf()
): List<EvaluationFlag> {
Expand Down
15 changes: 8 additions & 7 deletions sdk/src/main/java/com/amplitude/experiment/FlagApi.kt
Original file line number Diff line number Diff line change
@@ -1,28 +1,29 @@
package com.amplitude.experiment

import com.amplitude.experiment.evaluation.EvaluationFlag
import com.amplitude.experiment.evaluation.json
import com.amplitude.experiment.util.AsyncFuture
import com.amplitude.experiment.util.Logger
import com.amplitude.experiment.util.toFlag
import kotlinx.serialization.decodeFromString
import okhttp3.*
import okhttp3.HttpUrl.Companion.toHttpUrl
import org.json.JSONArray
import java.io.IOException
import java.util.concurrent.Future
import java.util.concurrent.TimeUnit

data class GetFlagsOptions(
internal data class GetFlagsOptions(
val libraryName: String,
val libraryVersion: String,
val evaluationMode: String? = null,
val timeoutMillis: Long = ExperimentConfig.Defaults.FETCH_TIMEOUT_MILLIS
)

interface FlagApi {
internal interface FlagApi {
fun getFlags(options: GetFlagsOptions? = null): Future<Map<String, EvaluationFlag>>
}

class SdkFlagApi(
internal class SdkFlagApi(
private val deploymentKey: String,
private val serverUrl: String,
private val httpClient: OkHttpClient
Expand Down Expand Up @@ -56,10 +57,10 @@ class SdkFlagApi(
val jsonArray = JSONArray(body)
val flags = mutableMapOf<String, EvaluationFlag>()
(0 until jsonArray.length()).forEach {
val json = jsonArray.getJSONObject(it)
val flag = json.toFlag()
val jsonString = jsonArray.getString(it)
val flag = json.decodeFromString<EvaluationFlag>(jsonString)
if (flag != null) {
flags[json.getString("key")] = flag
flags[flag.key] = flag
}
}

Expand Down
34 changes: 18 additions & 16 deletions sdk/src/main/java/com/amplitude/experiment/storage/Cache.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,17 @@ package com.amplitude.experiment.storage

import com.amplitude.experiment.Variant
import com.amplitude.experiment.evaluation.EvaluationFlag
import com.amplitude.experiment.util.toFlag
import com.amplitude.experiment.evaluation.json
import com.amplitude.experiment.util.toVariant
import com.amplitude.experiment.util.toJson
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.encodeToString

internal class LoadStoreCache<V : Any>(
internal class LoadStoreCache<V>(
private val namespace: String,
private val storage: Storage,
private val transformer: ((value: String) -> V?)
private val decoder: ((value: String) -> V?),
private val encoder: ((value: V) -> String)
) {
private val cache: MutableMap<String, V> = mutableMapOf()

Expand Down Expand Up @@ -41,7 +44,7 @@ internal class LoadStoreCache<V : Any>(
val rawValues = storage.get(namespace)
val values = rawValues.mapNotNull { entry ->
try {
val value = transformer.invoke(entry.value)
val value = decoder.invoke(entry.value)
if (value != null) {
entry.key to value
} else {
Expand All @@ -58,7 +61,7 @@ internal class LoadStoreCache<V : Any>(
fun store(values: MutableMap<String, V> = cache) = synchronized(cache) {
val stringValues = values.mapNotNull { entry ->
try {
val value = transformStringFromV(entry.value)
val value = encoder(entry.value)
if (value != null) {
entry.key to value
} else {
Expand All @@ -75,7 +78,7 @@ internal class LoadStoreCache<V : Any>(
internal fun getVariantStorage(deploymentKey: String, instanceName: String, storage: Storage): LoadStoreCache<Variant> {
val truncatedDeployment = deploymentKey.takeLast(6)
val namespace = "amp-exp-$instanceName-$truncatedDeployment"
return LoadStoreCache(namespace, storage, ::transformVariantFromStorage)
return LoadStoreCache(namespace, storage, ::decodeVariantFromStorage, ::encodeVariantToStorage)
}

internal fun getFlagStorage(
Expand All @@ -85,22 +88,21 @@ internal fun getFlagStorage(
): LoadStoreCache<EvaluationFlag> {
val truncatedDeployment = deploymentKey.takeLast(6)
val namespace = "amp-exp-$instanceName-$truncatedDeployment-flags"
return LoadStoreCache(namespace, storage, ::transformFlagFromStorage)
return LoadStoreCache(namespace, storage, ::decodeFlagFromStorage, ::encodeFlagToStorage)
}

internal fun transformVariantFromStorage(storageValue: String): Variant? {
internal fun decodeVariantFromStorage(storageValue: String): Variant? {
return storageValue.toVariant()
}

internal fun transformFlagFromStorage(storageValue: String): EvaluationFlag? {
return storageValue.toFlag()
internal fun decodeFlagFromStorage(storageValue: String): EvaluationFlag? {
return json.decodeFromString<EvaluationFlag>(storageValue)
}

internal fun transformStringFromV(value: Any): String? {
return when (value) {
is Variant -> value.toJson()
is EvaluationFlag -> value.toJson()
else -> null
}
internal fun encodeVariantToStorage(value: Variant): String {
return value.toJson()
}

internal fun encodeFlagToStorage(value: EvaluationFlag): String {
return json.encodeToString(value)
}
62 changes: 0 additions & 62 deletions sdk/src/main/java/com/amplitude/experiment/util/Flag.kt

This file was deleted.

Loading

0 comments on commit 606c306

Please sign in to comment.