All Downloads are FREE. Search and download functionalities are using the official Maven repository.

flag.FlagConfigService.kt Maven / Gradle / Ivy

package com.amplitude.experiment.flag

import com.amplitude.experiment.evaluation.EvaluationMode
import com.amplitude.experiment.evaluation.FlagConfig
import com.amplitude.experiment.util.Logger
import com.amplitude.experiment.util.Once
import java.util.concurrent.Executors
import java.util.concurrent.TimeUnit
import java.util.concurrent.locks.ReentrantReadWriteLock
import kotlin.concurrent.read
import kotlin.concurrent.write

internal data class FlagConfigServiceConfig(
    val flagConfigPollerIntervalMillis: Long,
)

internal typealias FlagConfigInterceptor = (Map) -> Unit

internal interface FlagConfigService {
    fun start()
    fun stop()
    fun getFlagConfigs(keys: List = listOf()): List
    fun addFlagConfigInterceptor(listener: FlagConfigInterceptor)
}

internal class FlagConfigServiceImpl(
    private val config: FlagConfigServiceConfig,
    private val flagConfigApi: FlagConfigApi,
    private val flagConfigStorage: FlagConfigStorage,
) : FlagConfigService {

    private val lock = Once()
    private val poller = Executors.newSingleThreadScheduledExecutor()

    private val interceptorsLock = ReentrantReadWriteLock()
    private val interceptors: MutableSet = mutableSetOf()

    private fun refresh() {
        Logger.d("Refreshing flag configs.")
        val flagConfigs = getFlagConfigs()
        storeFlagConfigs(flagConfigs)
        Logger.d("Refreshed ${flagConfigs.size} flag configs.")
    }

    override fun start() {
        lock.once {
            refresh()
            poller.scheduleAtFixedRate(
                { refresh() },
                config.flagConfigPollerIntervalMillis,
                config.flagConfigPollerIntervalMillis,
                TimeUnit.MILLISECONDS
            )
        }
    }

    override fun stop() {
        poller.shutdown()
    }

    override fun getFlagConfigs(keys: List): List {
        return if (keys.isEmpty()) {
            flagConfigStorage.getAll().values.toList()
        } else {
            keys.mapNotNull { flagConfigStorage.get(it) }
        }
    }

    override fun addFlagConfigInterceptor(listener: FlagConfigInterceptor) {
        interceptorsLock.write {
            interceptors += listener
        }
    }

    private fun getFlagConfigs(): Map {
        return flagConfigApi.getFlagConfigs(GetFlagConfigsRequest(EvaluationMode.LOCAL)).get().apply {
            interceptorsLock.read {
                interceptors.forEach { interceptor ->
                    interceptor.invoke(this)
                }
            }
        }
    }

    private fun storeFlagConfigs(flagConfigs: Map) {
        flagConfigStorage.overwrite(flagConfigs)
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy