Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Service-Level Cache

Caelix cache support is explicit service-level caching. It does not add automatic HTTP response caching.

Import CacheModule and inject Cache into a service:

#![allow(unused)]
fn main() {
use std::{sync::Arc, time::Duration};

use caelix::{Cache, CacheModule, Module, ModuleMetadata, Result, injectable};

pub struct AppModule;

impl Module for AppModule {
    fn register() -> ModuleMetadata {
        ModuleMetadata::new()
            .import::<CacheModule>()
            .import::<UsersModule>()
    }
}

#[injectable]
pub struct UsersService {
    cache: Arc<Cache>,
}

impl UsersService {
    pub async fn find_cached(&self, id: i64) -> Result<Option<UserDto>> {
        let key = format!("users:{id}");
        if let Some(user) = self.cache.get(&key).await? {
            return Ok(Some(user));
        }

        let user = self.find(id).await?;
        if let Some(user) = &user {
            self.cache.set_with_ttl(&key, user, Duration::from_secs(60)).await?;
        }

        Ok(user)
    }
}
}

Available methods:

#![allow(unused)]
fn main() {
cache.get::<UserDto>("users:1").await?;
cache.set("users:1", &user).await?;
cache.set_with_ttl("users:1", &user, Duration::from_secs(60)).await?;
cache.set_with_optional_ttl("users:1", &user, Some(Duration::from_secs(60))).await?;
cache.delete("users:1").await?;
cache.clear().await?;
}

Values are serialized to serde_json::Value before storage and deserialized on get.

CacheModule registers MemoryCacheStore and Cache. MemoryCacheStore supports maximum entries, maximum serialized value size, and an optional default TTL through MemoryCacheOptions.

#![allow(unused)]
fn main() {
use std::time::Duration;

use caelix::{Cache, MemoryCacheOptions, MemoryCacheStore, Module, ModuleMetadata, provider_dependencies};

pub struct CacheConfigModule;

impl Module for CacheConfigModule {
    fn register() -> ModuleMetadata {
        ModuleMetadata::new()
            .provider_async_factory::<MemoryCacheStore, _, _>(provider_dependencies![], |_container| async {
                Ok::<_, std::convert::Infallible>(
                    MemoryCacheStore::with_options(MemoryCacheOptions {
                        max_entries: 10_000,
                        max_value_bytes: 2 * 1024 * 1024,
                        default_ttl: Some(Duration::from_secs(300)),
                    }),
                )
            })
            .provider::<Cache>()
    }
}
}

When the cache reaches max_entries, the memory store removes the oldest inserted entries. Expired entries are removed during cache writes and missed on reads.

To use a custom backend, implement CacheStore and register Cache with a factory that constructs Cache::new(Arc<dyn CacheStore>).

#![allow(unused)]
fn main() {
use serde_json::Value;
use std::{sync::Arc, time::Duration};

use caelix::{BoxFuture, Cache, CacheStore, Module, ModuleMetadata, Result};

pub struct RedisCacheStore;

impl CacheStore for RedisCacheStore {
    fn get(&self, key: String) -> BoxFuture<'_, Result<Option<Value>>> {
        Box::pin(async move {
            // load JSON value by key
            Ok(None)
        })
    }

    fn set(&self, key: String, value: Value, ttl: Option<Duration>) -> BoxFuture<'_, Result<()>> {
        Box::pin(async move {
            // write JSON value with optional TTL
            Ok(())
        })
    }

    fn delete(&self, key: String) -> BoxFuture<'_, Result<()>> {
        Box::pin(async move { Ok(()) })
    }

    fn clear(&self) -> BoxFuture<'_, Result<()>> {
        Box::pin(async move { Ok(()) })
    }
}

pub struct RedisCacheModule;

impl Module for RedisCacheModule {
    fn register() -> ModuleMetadata {
        ModuleMetadata::new()
            .provider_async_factory::<Cache, _, _>(provider_dependencies![], |_container| async move {
                let store: Arc<dyn CacheStore> = Arc::new(RedisCacheStore);
                Ok::<_, std::convert::Infallible>(Cache::new(store))
            })
    }
}
}