1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
#![warn(missing_docs)]
//! # Hitbox-Actix
//!
//! [![Build status](https://github.com/hit-box/hitbox/actions/workflows/CI.yml/badge.svg)](https://github.com/hit-box/hitbox/actions?query=workflow)
//! [![Coverage Status](https://codecov.io/gh/hit-box/hitbox/branch/master/graph/badge.svg?token=tgAm8OBLkY)](https://codecov.io/gh/hit-box/hitbox)
//!
//! Hitbox-Actix is an asynchronous caching framework for [Actix] actor framework.
//! It's designed for distributed and for single-machine applications.
//!
//! ## Features
//! - [x] Automatic cache key generation.
//! - [x] Multiple cache backend implementations.
//! - [x] Stale cache mechanics.
//! - [ ] Cache locks for [dogpile effect] preventions.
//! - [ ] Distributed cache locks.
//! - [ ] Detailed metrics out of the box.
//!
//! ## Backend implementations:
//! - [x] [Redis](https://github.com/hit-box/hitbox/tree/master/hitbox-backend)
//! - [ ] 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](https://docs.rs/serde_qs/latest/serde_qs/#supported-types).
//!
//! ## Documentation
//! * [API Documentation](https://docs.rs/hitbox_acitx/)
//! * [Examples](https://github.com/hit-box/hitbox/tree/master/examples/examples)
//!
//! ### Flow diagrams:
//! [![Simple flow](http://www.plantuml.com/plantuml/proxy?src=https://raw.githubusercontent.com/hit-box/hitbox/master/documentation/simple_flow.puml)](http://www.plantuml.com/plantuml/proxy?src=https://raw.githubusercontent.com/hit-box/hitbox/master/documentation/simple_flow.puml)
//!
//! ## Example
//!
//! ### Dependencies:
//!
//! ```toml
//! [dependencies]
//! hitbox_actix = "0.1"
//! ```
//!
//! ### Code:
//!
//! First, you should derive [Cacheable] trait for your actix [Message]:
//!
//! ```rust,ignore
//! 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:
//!
//! ```rust,ignore
//! #[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:
//!
//! ```rust,ignore
//! 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(())
//! }
//! ```
//!
//! [Cacheable]: hitbox::Cacheable
//! [CacheableResponse]: hitbox::CacheableResponse
//! [Backend]: hitbox_backend::Backend
//! [RedisBackend]: hitbox_redis::RedisActor
//! [dogpile effect]: https://www.sobstel.org/blog/preventing-dogpile-effect/
//! [Message]: actix::Message
//! [Actix]: https://github.com/actix/actix/

pub mod actor;
pub mod builder;
pub mod handlers;
pub mod messages;
pub mod runtime;

pub use actor::CacheActor;
pub use builder::CacheBuilder;
pub use hitbox::{CacheError, Cacheable};
pub use messages::{IntoCache, QueryCache};
pub use runtime::ActixAdapter;

#[cfg(feature = "redis")]
pub use hitbox_redis::RedisBackend;

/// Default type alias with RedisBackend.
/// You can disable it or define it manually in your code.
#[cfg(feature = "redis")]
pub type Cache = CacheActor<RedisBackend>;

/// Prelude for hitbox_actix.
pub mod prelude {
    #[cfg(feature = "redis")]
    pub use crate::{Cache, RedisBackend};
    pub use crate::{CacheActor, CacheBuilder, CacheError, Cacheable, IntoCache, QueryCache};
    pub use hitbox::hitbox_serializer;
}