Crate hitbox_actix

Source
Expand description

§Hitbox-Actix

Build status Coverage Status

Hitbox-Actix is an asynchronous caching framework for Actix actor framework. It’s designed for distributed and for single-machine applications.

§Features

  • Automatic cache key generation.
  • Multiple cache backend implementations.
  • Stale cache mechanics.
  • Cache locks for dogpile effect preventions.
  • Distributed cache locks.
  • Detailed metrics out of the box.

§Backend implementations:

  • Redis
  • In-memory backend

§Feature flags

  • derive - Support for Cacheable trait derive macros.
  • redis - Support for default redis backend.

§Restrictions

Default cache key implementation based on serde_qs crate and have some restrictions.

§Documentation

§Flow diagrams:

Simple flow

§Example

§Dependencies:

[dependencies]
hitbox_actix = "0.1"

§Code:

First, you should derive Cacheable trait for your actix Message:

use actix::prelude::*;
use actix_derive::{Message, MessageResponse};
use hitbox_actix::prelude::*;
use serde::{Deserialize, Serialize};

#[derive(Message, Cacheable, Serialize)]
#[rtype(result = "Result<Pong, Error>")]
struct Ping {
    id: i32,
}

#[derive(MessageResponse, Deserialize, Serialize, Debug)]
struct Pong(i32);

#[derive(Debug)]
struct Error;

Next step is declare Upstream actor and implement actix Handler for Ping:

#[derive(Debug)]
struct UpstreamActor;

impl Actor for UpstreamActor {
    type Context = Context<Self>;
}

impl Handler<Ping> for UpstreamActor {
    type Result = ResponseFuture<<Ping as Message>::Result>;

    fn handle(&mut self, msg: Ping, _ctx: &mut Self::Context) -> Self::Result {
        println!("Handler::Ping");
        Box::pin(async move {
            actix_rt::time::sleep(core::time::Duration::from_secs(3)).await;
            Ok(Pong(msg.id))
        })
    }
}

The last step is initialize and start CacheActor and UpstreamActor:

use tracing_subscriber::EnvFilter;

#[actix_rt::main]
async fn main() -> Result<(), CacheError> {
    let filter = EnvFilter::new("hitbox=trace");
    tracing_subscriber::fmt()
        .with_max_level(tracing::Level::TRACE)
        .with_env_filter(filter)
        .init();

    let backend = RedisBackend::new()
        .await?
        .start();

    let cache = Cache::builder()
        .with_stale()
        .finish(backend)
        .start();
    let upstream = UpstreamActor.start();

    /// And send `Ping` message into cache actor
    let msg = Ping { id: 42 };
    let res = cache.send(msg.into_cache(&upstream)).await??;
    println!("{:#?}", res);
    Ok(())
}

Re-exports§

pub use actor::CacheActor;
pub use builder::CacheBuilder;
pub use messages::IntoCache;
pub use messages::QueryCache;
pub use runtime::ActixAdapter;

Modules§

actor
Cache actor and Builder.
builder
CacheActor builder patter implementation.
handlers
Actix Handler implementation.
messages
QueryCache message declaration and converting.
prelude
Prelude for hitbox_actix.
runtime
hitbox::runtime::RuntimeAdapter implementation for Actix runtime.

Structs§

RedisBackend
Redis cache backend based on redis-rs crate.

Enums§

CacheError
Base hitbox error.

Traits§

Cacheable
Trait describes cache configuration per type that implements this trait.

Type Aliases§

Cache
Default type alias with RedisBackend. You can disable it or define it manually in your code.

Derive Macros§

Cacheable
Derive Cacheable macro implementation.