Skip to content

IngotGG/angostura

Repository files navigation

angostura

Angostura, a fast caching layer.


Angostura

Angostura is a quick and easy caching layer used by ingot.gg backend services to handle heavy database load and used for general caching.

Feel free to read the Contribution Guide to learn how to contribute to Angostura or report issues.

Importing

Tags & Releases can be found on our Jitpack.

Gradle

repositories {
  maven("https://jitpack.io")
}

dependencies {
    implementation("gg.ingot.angostura:angostura-core:TAG")
}

Maven

<repository>
    <id>jitpack.io</id>
    <url>https://jitpack.io</url>
</repository>

<dependency>
    <groupId>gg.ingot.angostura</groupId>
    <artifactId>angostura-core</artifactId>
    <version>TAG</version>
</dependency>

Features

Basic Usage

Memory Cache

suspend fun main() {
    val angostura = Angostura()
    
    val cache = angostura.memoryCache<String>(ttl = 5.seconds)
    val value = cache.getOrCache("key") {
        "value"
    }
    
    println(value)
}

Jedis

Jedis is supported as an extension package of Angostura.

suspend fun main() {
    val jedis = JedisPool("localhost", 6379)

    val angostura = Angostura(
        AngosturaSettings().withExtra(AngosturaJedisSettings( // we pass the extra config
            pool = jedis
        ))
    )
    
    // we can use it in exactly the same way as the memory cache
    val cache = angostura.jedisCache<String>(ttl = 5.seconds, refreshTTL = true)
    val value = cache.getOrCache("key") {
        "value"
    }
    
    println(value)
}

Serialization

Serialization is usually used by out-of-memory caches like Redis.

@Serializable
data class User(val id: Int, val name: String)

suspend fun main() {
    val jedis = JedisPool("localhost", 6379)

    val angostura = Angostura(
        AngosturaSettings(
            // simply pass a serialization adapter
            serializationAdapter = AngosturaSerializationAdapter.Kotlinx(Json)
        ).withExtra(AngosturaJedisSettings( // we pass the extra config
            pool = jedis
        ))
    )

    // we can use it in exactly the same way as the memory cache
    val cache = angostura.jedisCache<User>(ttl = 5.seconds, refreshTTL = true)
    // Will automatically serialize and deserialize the object
    val value = cache.getOrCache(1.toString()) {
        User(1, "Ingot")
    }

    println(value)
}

Burst Requests

@Serializable
data class HotResource(val id: Int, val randomData: String)

suspend fun main() {
    val jedis = JedisPool("localhost", 6379)

    val angostura = Angostura(
        AngosturaSettings(
            // simply pass a serialization adapter
            serializationAdapter = AngosturaSerializationAdapter.Kotlinx(Json)
        ).withExtra(AngosturaJedisSettings( // we pass the extra config
            pool = jedis
        ))
    )

    // Burst Caches are useful for resources that are requested frequently
    // and aren't too important to constantly be up-to-date.
    val cache = angostura.burstCache<HotResource>(
        // first hit the minor cache which should be faster
        minorCache = angostura.memoryCache(ttl = 2.seconds),
        // then fallback to the major cache
        majorCache = angostura.jedisCache(ttl = 3.minutes, refreshTTL = true)
    )
    val value = cache.getOrCache(1.toString()) {
        HotResource(1, "Ingot Team")
    }

    println(value)
}

Cache Versioning

In cases where you may have multiple different versions of an app hitting the cache sometimes the models between those caches may differ which could lead to deserialization errors.

Applying a version key to the cache will allow you to safely rollback the application in these cases.

@Serializable
data class Resource(val id: Int)

suspend fun main() {
    val jedis = JedisPool("localhost", 6379)

    val angostura = Angostura(
        AngosturaSettings(
            // we can pass a version key for the cache
            version = System.getenv("GITHUB_COMMIT_HASH"),
            // simply pass a serialization adapter
            serializationAdapter = AngosturaSerializationAdapter.Kotlinx(Json)
        ).withExtra(AngosturaJedisSettings( // we pass the extra config
            pool = jedis
        ))
    )

    // the version will automatically be placed into the key and in redis it
    // will look something like this, 'angostura:cache:Resource:8c4db91:{KEY}'
    // so you can safely rollback the application or keep multiple versions of the
    // application running at once.
    val cache = angostura.jedisCache<Resource>(ttl = 5.seconds, refreshTTL = true)
    val value = cache.getOrCache("key") {
        Resource(id = 1)
    }

    println(value)
}