1 unstable release
0.1.1 | May 29, 2021 |
---|---|
0.1.0 |
|
#2 in #cache-backend
93KB
1.5K
SLoC
hitbox-actix
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:
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(())
}
Dependencies
~11MB
~183K SLoC