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

com.lightningkite.lightningserver.cache.Cache.kt Maven / Gradle / Ivy

The newest version!
package com.lightningkite.lightningserver.cache

import com.lightningkite.lightningserver.metrics.Metricable
import com.lightningkite.lightningserver.serialization.Serialization
import com.lightningkite.lightningserver.serverhealth.HealthCheckable
import com.lightningkite.lightningserver.serverhealth.HealthStatus
import kotlinx.serialization.KSerializer
import kotlinx.serialization.serializer
import java.time.Duration

@Deprecated("Renamed to just 'cache'", ReplaceWith("Cache", "com.lightningkite.lightningserver.cache.Cache"))
typealias CacheInterface = Cache

/**
 * An abstracted model for communicating with a Cache.
 * Every implementation will handle how to get and set values in the underlying cache system.
 */
interface Cache : HealthCheckable, Metricable {

    /**
     * Returns a value of type T from the cache.
     *
     * @param key The key that will be used to retrieve the value
     * @param serializer The serializer that will be used to turn the raw serialized data from the cache into T.
     * @return An instance of T, or null if the key did not exist in the cache.
     */
    suspend fun  get(key: String, serializer: KSerializer): T?

    /**
     * Sets the instance of T provided into the cache under the key provided. If the key already exists the existing data will be overwritten.
     * You can optionally provide an expiration time on the key. After that duration the key will automatically be removed.
     *
     * @param key The key that will be used when placing the value into the database.
     * @param value The instance of T that you wish to store into the cache.
     * @param serializer The serializer that will be used to turn the instance of T into serialized data to be stored in the cache.
     * @param timeToLive  (Optional) The expiration time to be set on for the key in the cache. If no value is provided the key will have no expiration time.
     */
    suspend fun  set(key: String, value: T, serializer: KSerializer, timeToLive: Duration? = null)

    /**
     * Sets the instance of T provided into the cache under the key provided. If the key already exists then the incoming value will not be added to the cache.
     * You can optionally provide an expiration time on the key. After that duration the key will automatically be removed.
     *
     * @param key The key that will be used when placing the value into the database.
     * @param value The instance of T that you wish to store into the cache.
     * @param serializer The serializer that will be used to turn the instance of T into serialized data to be stored in the cache.
     * @param timeToLive  (Optional) The expiration time to be set on for the key in the cache. If no value is provided the key will have no expiration time.
     */
    suspend fun  setIfNotExists(
        key: String,
        value: T,
        serializer: KSerializer,
        timeToLive: Duration? = null
    ): Boolean

    /**
     * Will modify an existing value in the cache. If the key does not exist and the modifcation still returns a value
     * then the new value will be inserted into the cache.
     *
     * @param key The key that will be used when modifying the value into the database.
     * @param serializer The serializer that will be used to turn the instance of T into serialized data to be stored in the cache.
     * @param maxTries How many times it will attempt to make the modification to the cache.
     * @param timeToLive  (Optional) The expiration time to be set on for the key in the cache. If no value is provided the key will have no expiration time.
     * @param modification A lambda that takes in a nullable T and returns a nullable T. If a non null value is returned it will be set in the cache using the key. If a null value is returned the key will be removed from the cache.
     */
    suspend fun  modify(
        key: String,
        serializer: KSerializer,
        maxTries: Int = 1,
        timeToLive: Duration? = null,
        modification: (T?) -> T?
    ): Boolean {
        repeat(maxTries) {
            val current = get(key, serializer)
            val new = modification(current)
            if (current == get(key, serializer)) {
                if (new != null)
                    set(key, new, serializer, timeToLive)
                else
                    remove(key)
                return true
            }
        }
        return false
    }


    /**
     * Updates the value under key by adding value to the numerical value stored in the cache.
     *
     * @param key The key that will be used when updating the value into the database.
     * @param value The Int you wish to add to the value already in the cache.
     * @param timeToLive (Optional) The expiration time to be set on for the key in the cache. If no value is provided the key will have no expiration time.
     */
    suspend fun add(key: String, value: Int, timeToLive: Duration? = null)


    /**
     * WARNING: A Dangerous call if you aren't sure.
     * Will remove EVERY key in the cache leaving you with an entirely empty cache.
     */
    suspend fun clear()

    /**
     * Removes a single key from cache. If the key didn't exist, nothing will happen.
     *
     * @param key The key that will be removed from the cache.
     */
    suspend fun remove(key: String)

    /**
     * Will attempt inserting data into the cache to confirm that the connection is alive and available.
     */
    override suspend fun healthCheck(): HealthStatus {
        return try {
            set("health-check-test-key", true)
            if (get("health-check-test-key") == true) {
                HealthStatus(HealthStatus.Level.OK)
            } else {
                HealthStatus(HealthStatus.Level.ERROR, additionalMessage = "Could not retrieve set property")
            }
        } catch (e: Exception) {
            HealthStatus(HealthStatus.Level.ERROR, additionalMessage = e.message)
        }
    }

    override fun withMetrics(metricsKeyName: String): Cache = MetricsCache(this, metricsKeyName)
}

/**
 * A Helper function for the underlying get call.
 * This can make get calls much cleaner and less wordy when the types can be inferred.
 */
suspend inline fun  Cache.get(key: String): T? {
    return get(key, Serialization.Internal.json.serializersModule.serializer())
}

/**
 * A Helper function for the underlying set call.
 * This can make set calls much cleaner and less wordy when the types can be inferred.
 */
suspend inline fun  Cache.set(key: String, value: T, timeToLive: Duration? = null) {
    return set(key, value, Serialization.Internal.json.serializersModule.serializer(), timeToLive)
}

/**
 * A Helper function for the underlying set setIfNotExists call.
 * This can make setIfNotExists calls much cleaner and less wordy when the types can be inferred.
 */
suspend inline fun  Cache.setIfNotExists(
    key: String,
    value: T,
    timeToLive: Duration? = null
): Boolean {
    return setIfNotExists(key, value, Serialization.Internal.json.serializersModule.serializer(), timeToLive)
}


/**
 * A Helper function for the underlying set modify call.
 * This can make modify calls much cleaner and less wordy when the types can be inferred.
 */
suspend inline fun  Cache.modify(
    key: String,
    maxTries: Int = 1,
    timeToLive: Duration? = null,
    noinline modification: (T?) -> T?
): Boolean =
    modify(key, Serialization.Internal.json.serializersModule.serializer(), maxTries, timeToLive, modification)




© 2015 - 2024 Weber Informatics LLC | Privacy Policy