From e747d881bdd8db17f45c9c19672de260482590f5 Mon Sep 17 00:00:00 2001 From: Denis Kayshev Date: Sat, 29 Jul 2023 19:13:28 +0300 Subject: [PATCH] redis: restruct dirs --- examples/examples/redis.rs | 2 +- hitbox-redis/Cargo.toml | 2 - hitbox-redis/src/{fred => }/backend.rs | 32 ++--- hitbox-redis/src/builder/cluster.rs | 32 +++++ hitbox-redis/src/builder/mod.rs | 23 ++++ hitbox-redis/src/builder/sentinel.rs | 26 ++++ hitbox-redis/src/builder/standalone.rs | 119 ++++++++++++++++++ hitbox-redis/src/error.rs | 16 +-- hitbox-redis/src/fred/builder.rs | 153 ----------------------- hitbox-redis/src/fred/mod.rs | 6 - hitbox-redis/src/lib.rs | 22 ++-- hitbox-redis/src/redis/backend.rs | 161 ------------------------- hitbox-redis/src/redis/builder.rs | 0 hitbox-redis/src/redis/mod.rs | 2 - 14 files changed, 222 insertions(+), 374 deletions(-) rename hitbox-redis/src/{fred => }/backend.rs (79%) create mode 100644 hitbox-redis/src/builder/cluster.rs create mode 100644 hitbox-redis/src/builder/mod.rs create mode 100644 hitbox-redis/src/builder/sentinel.rs create mode 100644 hitbox-redis/src/builder/standalone.rs delete mode 100644 hitbox-redis/src/fred/builder.rs delete mode 100644 hitbox-redis/src/fred/mod.rs delete mode 100644 hitbox-redis/src/redis/backend.rs delete mode 100644 hitbox-redis/src/redis/builder.rs delete mode 100644 hitbox-redis/src/redis/mod.rs diff --git a/examples/examples/redis.rs b/examples/examples/redis.rs index 7db8c15..51113f4 100644 --- a/examples/examples/redis.rs +++ b/examples/examples/redis.rs @@ -1,6 +1,6 @@ use axum::{extract::Path, routing::get, Json, Router}; -use hitbox_redis::fred::Builder; +use hitbox_redis::Builder; use hitbox_tower::Cache; use http::StatusCode; use tower::ServiceBuilder; diff --git a/hitbox-redis/Cargo.toml b/hitbox-redis/Cargo.toml index 9ae15d1..f59cae4 100644 --- a/hitbox-redis/Cargo.toml +++ b/hitbox-redis/Cargo.toml @@ -12,8 +12,6 @@ keywords = ["cache", "async", "cache-backend", "hitbox", "redis"] [dependencies] hitbox-backend = { path = "../hitbox-backend", version = "0.1.0" } -log = "0.4" -redis = { version = "0.22", features = ["tokio-comp", "connection-manager"] } thiserror = "1" async-trait = "0.1" serde = "1" diff --git a/hitbox-redis/src/fred/backend.rs b/hitbox-redis/src/backend.rs similarity index 79% rename from hitbox-redis/src/fred/backend.rs rename to hitbox-redis/src/backend.rs index ccebf39..fb41087 100644 --- a/hitbox-redis/src/fred/backend.rs +++ b/hitbox-redis/src/backend.rs @@ -1,5 +1,3 @@ -#![allow(missing_docs)] - use std::future::Future; use std::marker::PhantomData; @@ -9,22 +7,12 @@ use fred::{ interfaces::{ClientLike, KeysInterface}, types::{Expiration, FromRedis, RedisKey, RedisValue}, }; -use serde::Serialize; - use hitbox_backend::{ serializer::Serializer, BackendError, BackendResult, CacheBackend, CacheableResponse, CachedValue, DeleteStatus, }; -#[derive(Debug, thiserror::Error)] -pub enum RedisBackendError { - #[error(transparent)] - Fred(#[from] fred::error::RedisError), - #[error(transparent)] - Tokio(#[from] tokio::task::JoinError), - #[error("Setup buidler error: {0}")] - Builder(String), -} +use crate::error::Error; #[derive(Clone)] pub struct RedisBackend { @@ -33,10 +21,10 @@ pub struct RedisBackend { } impl RedisBackend { - async fn execute<'a, T, Fut, F>(&'a self, f: F) -> Result + async fn execute<'a, T, Fut, F>(&'a self, f: F) -> Result where F: FnOnce(&'a RedisClient) -> Fut, - Fut: Future>, + Fut: Future>, T: Send, { let connection_task = self.client.connect(); @@ -68,10 +56,9 @@ where client .get::, _>(key) .await - .map_err(RedisBackendError::from) + .map_err(Error::from) }) .await - .map_err(crate::error::Error::from) .map_err(BackendError::from)?; result .map(|value| S::deserialize(value).map_err(BackendError::from)) @@ -86,7 +73,7 @@ where ) -> BackendResult<()> where T: CacheableResponse + Send, - ::Cached: Serialize + Send + Sync, + ::Cached: serde::Serialize + Send + Sync, { tracing::debug!("RedisBackend::set::{}", &key); let key = RedisKey::from(key); @@ -96,15 +83,14 @@ where client .set::<(), _, S::Raw>(key, ser_value, expire, None, false) .await - .map_err(RedisBackendError::from) + .map_err(Error::from) }) .await - .map_err(crate::error::Error::from) .map_err(BackendError::from) } async fn delete(&self, key: String) -> BackendResult { - tracing::debug!("RedisBackend::::{}", &key); + tracing::debug!("RedisBackend::delete::{}", &key); let key = RedisKey::from(key); self.execute(|client| async move { client @@ -117,14 +103,14 @@ where DeleteStatus::Missing } }) - .map_err(RedisBackendError::from) + .map_err(Error::from) }) .await - .map_err(crate::error::Error::from) .map_err(BackendError::from) } async fn start(&self) -> BackendResult<()> { + tracing::debug!("RedisBackend::start"); Ok(()) } } diff --git a/hitbox-redis/src/builder/cluster.rs b/hitbox-redis/src/builder/cluster.rs new file mode 100644 index 0000000..1f6b9f2 --- /dev/null +++ b/hitbox-redis/src/builder/cluster.rs @@ -0,0 +1,32 @@ +use std::marker::PhantomData; + +use hitbox_backend::serializer::{JsonSerializer, Serializer}; + +use crate::backend::RedisBackend; +use crate::error::Error; + +struct ClusterNode {} + +pub struct Cluster> { + nodes: Vec, + _ser: PhantomData, +} + +impl Default for Cluster { + fn default() -> Self { + Self { + nodes: vec![], + _ser: PhantomData, + } + } +} + +impl Cluster { + pub fn new() -> Self { + Cluster::default() + } + + pub fn build(self) -> Result, Error> { + unimplemented!() + } +} diff --git a/hitbox-redis/src/builder/mod.rs b/hitbox-redis/src/builder/mod.rs new file mode 100644 index 0000000..a9bf3e2 --- /dev/null +++ b/hitbox-redis/src/builder/mod.rs @@ -0,0 +1,23 @@ +mod cluster; +mod sentinel; +mod standalone; + +use cluster::Cluster; +use sentinel::Sentinel; +use standalone::Standalone; + +pub struct Builder; + +impl Builder { + pub fn standalone() -> Standalone { + Standalone::default() + } + + pub fn sentinel() -> Sentinel { + Sentinel::default() + } + + pub fn cluster() -> Cluster { + Cluster::default() + } +} diff --git a/hitbox-redis/src/builder/sentinel.rs b/hitbox-redis/src/builder/sentinel.rs new file mode 100644 index 0000000..5d641b3 --- /dev/null +++ b/hitbox-redis/src/builder/sentinel.rs @@ -0,0 +1,26 @@ +use std::marker::PhantomData; + +use hitbox_backend::serializer::{JsonSerializer, Serializer}; + +use crate::backend::RedisBackend; +use crate::error::Error; + +pub struct Sentinel> { + _ser: PhantomData, +} + +impl Default for Sentinel { + fn default() -> Self { + Self { _ser: PhantomData } + } +} + +impl Sentinel { + pub fn new() -> Self { + Sentinel::default() + } + + pub fn build(self) -> Result, Error> { + unimplemented!() + } +} diff --git a/hitbox-redis/src/builder/standalone.rs b/hitbox-redis/src/builder/standalone.rs new file mode 100644 index 0000000..afffca3 --- /dev/null +++ b/hitbox-redis/src/builder/standalone.rs @@ -0,0 +1,119 @@ +use std::marker::PhantomData; + +use fred::clients::RedisClient; +use fred::types::{Blocking, RedisConfig, RespVersion, ServerConfig}; +use hitbox_backend::serializer::{JsonSerializer, Serializer}; + +use crate::backend::RedisBackend; +use crate::error::Error; + +pub struct Standalone> { + host: Option, + port: Option, + username: Option, + password: Option, + database: Option, + _ser: PhantomData, +} + +impl Default for Standalone { + fn default() -> Self { + Standalone { + username: None, + password: None, + host: Some("127.0.0.1".to_owned()), + port: Some(6379), + database: None, + _ser: PhantomData, + } + } +} + +impl Standalone { + pub fn from_url(connection_url: &str) -> Result, Error> { + let cfg = RedisConfig::from_url(connection_url)?; + if cfg.server.is_centralized() { + let server = cfg.server.hosts()[0]; + Ok(Self { + username: cfg.username, + password: cfg.password, + database: cfg.database, + host: Some(server.host.to_string()), + port: Some(server.port), + _ser: PhantomData, + }) + } else { + todo!() + } + } + + pub fn set_username(self, value: String) -> Self { + Standalone { + username: Some(value), + ..self + } + } + + pub fn set_password(self, value: String) -> Self { + Standalone { + password: Some(value), + ..self + } + } + + pub fn set_host(self, value: String) -> Self { + Standalone { + host: Some(value), + ..self + } + } + + pub fn set_port(self, value: u16) -> Self { + Standalone { + port: Some(value), + ..self + } + } + + pub fn set_database(self, value: u8) -> Self { + Standalone { + database: Some(value), + ..self + } + } + + pub fn with_serializer(self) -> Standalone { + Standalone { + username: self.username, + password: self.password, + host: self.host, + port: self.port, + database: self.database, + _ser: PhantomData::, + } + } + + pub fn build(self) -> Result, Error> { + let host = self + .host + .ok_or_else(|| Error::Builder("Please setup host".to_owned()))?; + let port = self + .port + .ok_or_else(|| Error::Builder("Please setup port".to_owned()))?; + let config = RedisConfig { + fail_fast: true, + server: ServerConfig::new_centralized(host, port), + blocking: Blocking::Block, + username: self.username, + password: self.password, + version: RespVersion::RESP2, + database: self.database, + }; + + let client = RedisClient::new(config, None, None); + Ok(RedisBackend { + client, + _ser: self._ser, + }) + } +} diff --git a/hitbox-redis/src/error.rs b/hitbox-redis/src/error.rs index b8bec30..5a139a4 100644 --- a/hitbox-redis/src/error.rs +++ b/hitbox-redis/src/error.rs @@ -2,20 +2,14 @@ //! //! [BackendError]: hitbox_backend::BackendError use hitbox_backend::BackendError; -use redis::RedisError; - -/// Redis backend error declaration. -/// -/// Simply, it's just a wrapper for [redis::RedisError]. -/// -/// [redis::RedisError]: redis::RedisError #[derive(Debug, thiserror::Error)] pub enum Error { - /// Wrapper for all kinds redis-rs errors. #[error("Redis backend error: {0}")] - Redis(#[from] RedisError), - #[error("Fred error: {0}")] - Fred(#[from] crate::fred::RedisBackendError), + Redis(#[from] fred::error::RedisError), + #[error("Builder error: {0}")] + Builder(String), + #[error(transparent)] + Tokio(#[from] tokio::task::JoinError), } impl From for BackendError { diff --git a/hitbox-redis/src/fred/builder.rs b/hitbox-redis/src/fred/builder.rs deleted file mode 100644 index 3292508..0000000 --- a/hitbox-redis/src/fred/builder.rs +++ /dev/null @@ -1,153 +0,0 @@ -#![allow(missing_docs)] - -use std::marker::PhantomData; - -use super::backend::RedisBackend; -use super::backend::RedisBackendError; - -use fred::clients::RedisClient; -use fred::types::ServerConfig; -use fred::types::{Blocking, RedisConfig, RespVersion}; -use hitbox_backend::serializer::{JsonSerializer, Serializer}; - -pub struct Builder; - -impl Builder { - pub fn standalone() -> StandaloneBuilder { - StandaloneBuilder::default() - } - - //pub fn sentinel() -> SentinelBuilder { - // unimplemented!() - //} - - //pub fn cluster() -> ClusterBuilder { - // unimplemented!() - //} -} - -pub struct StandaloneBuilder> { - host: Option, - port: Option, - username: Option, - password: Option, - database: Option, - _ser: PhantomData, -} - -impl Default for StandaloneBuilder { - fn default() -> Self { - StandaloneBuilder { - username: None, - password: None, - host: Some("127.0.0.1".to_owned()), - port: Some(6379), - database: None, - _ser: PhantomData, - } - } -} - -impl StandaloneBuilder { - pub fn from_url(connection_url: String) -> Result, RedisBackendError> { - unimplemented!() - } - - pub fn set_username(self, value: String) -> Self { - StandaloneBuilder { - username: Some(value), - ..self - } - } - - pub fn set_password(self, value: String) -> Self { - StandaloneBuilder { - password: Some(value), - ..self - } - } - - pub fn set_host(self, value: String) -> Self { - StandaloneBuilder { - host: Some(value), - ..self - } - } - - pub fn set_port(self, value: u16) -> Self { - StandaloneBuilder { - port: Some(value), - ..self - } - } - - pub fn set_database(self, value: u8) -> Self { - StandaloneBuilder { - database: Some(value), - ..self - } - } - - pub fn with_serializer(self) -> StandaloneBuilder { - StandaloneBuilder { - username: self.username, - password: self.password, - host: self.host, - port: self.port, - database: self.database, - _ser: PhantomData::, - } - } - - pub fn build(self) -> Result, RedisBackendError> { - let host = self - .host - .ok_or_else(|| RedisBackendError::Builder("Please setup host".to_owned()))?; - let port = self - .port - .ok_or_else(|| RedisBackendError::Builder("Please setup port".to_owned()))?; - let config = RedisConfig { - fail_fast: true, - server: ServerConfig::new_centralized(host, port), - blocking: Blocking::Block, - username: self.username, - password: self.password, - version: RespVersion::RESP2, - database: self.database, - }; - - let client = RedisClient::new(config, None, None); - Ok(RedisBackend { - client, - _ser: self._ser, - }) - } -} - -//pub struct SentinelBuilder> { -// connection_url: String, -// _p: PhantomData, -//} -// -//impl Default for SentinelBuilder { -// fn default() -> Self { -// Self { -// connection_url: "redis://127.0.0.1:6379/0".to_owned(), -// _p: PhantomData::>::default(), -// } -// } -//} -// -//pub struct ClusterBuilder> { -// connection_url: String, -// _p: PhantomData, -//} -// -//impl Default for ClusterBuilder { -// fn default() -> Self { -// Self { -// connection_url: "redis://127.0.0.1:6379/0".to_owned(), -// _p: std::marker::PhantomData::>::default(), -// } -// } -//} diff --git a/hitbox-redis/src/fred/mod.rs b/hitbox-redis/src/fred/mod.rs deleted file mode 100644 index e5788e3..0000000 --- a/hitbox-redis/src/fred/mod.rs +++ /dev/null @@ -1,6 +0,0 @@ -mod backend; -mod builder; - -pub use backend::RedisBackendError; -pub use builder::Builder; -pub use backend::RedisBackend; diff --git a/hitbox-redis/src/lib.rs b/hitbox-redis/src/lib.rs index a48d62e..811f290 100644 --- a/hitbox-redis/src/lib.rs +++ b/hitbox-redis/src/lib.rs @@ -1,19 +1,11 @@ -#![warn(missing_docs)] +// TODO: add docs +#![allow(missing_docs)] //! hitbox [Backend] implementation for Redis. -//! -//! This crate uses [redis-rs] as base library for asynchronous interaction with redis nodes. -//! It use one [MultiplexedConnection] for better connection utilization. -//! -//! [MultiplexedConnection]: redis::aio::MultiplexedConnection -//! [Backend]: hitbox_backend::Backend -//! [redis-rs]: redis-rs::aio -//pub mod backend; pub mod error; -//pub mod fred_backend; -pub mod fred; -mod redis; +mod backend; +mod builder; -//#[doc(inline)] -//pub use crate::backend::{RedisBackend, RedisBackendBuilder}; +pub use backend::RedisBackend; +pub use builder::Builder; +pub use error::Error; -//pub use crate::fred_backend::{Builder, RedisBackend}; diff --git a/hitbox-redis/src/redis/backend.rs b/hitbox-redis/src/redis/backend.rs deleted file mode 100644 index 00c671a..0000000 --- a/hitbox-redis/src/redis/backend.rs +++ /dev/null @@ -1,161 +0,0 @@ -//! Redis backend actor implementation. -use crate::error::Error; -use async_trait::async_trait; -use hitbox_backend::{ - serializer::{JsonSerializer, Serializer}, - BackendError, BackendResult, CacheBackend, CacheableResponse, CachedValue, DeleteStatus, -}; -use redis::{aio::ConnectionManager, Client}; -use tokio::sync::OnceCell; -use tracing::trace; - -/// Redis cache backend based on redis-rs crate. -/// -/// This struct provides redis as storage [Backend] for hitbox. -/// Its use one [MultiplexedConnection] for asynchronous network interaction. -/// -/// [MultiplexedConnection]: redis::aio::MultiplexedConnection -/// [Backend]: hitbox_backend::Backend -#[derive(Clone)] -pub struct RedisBackend { - client: Client, - connection: OnceCell, -} - -impl RedisBackend { - /// Create new backend instance with default settings. - /// - /// # Examples - /// ``` - /// use hitbox_redis::RedisBackend; - /// - /// #[tokio::main] - /// async fn main() { - /// let backend = RedisBackend::new(); - /// } - /// ``` - pub fn new() -> Result { - Ok(Self::builder().build()?) - } - - /// Creates new RedisBackend builder with default settings. - pub fn builder() -> RedisBackendBuilder { - RedisBackendBuilder::default() - } - - /// Create lazy connection to redis via [ConnectionManager](redis::aio::ConnectionManager) - pub async fn connection(&self) -> Result<&ConnectionManager, BackendError> { - trace!("Get connection manager"); - let manager = self - .connection - .get_or_try_init(|| { - trace!("Initialize new redis connection manager"); - self.client.get_tokio_connection_manager() - }) - .await - .map_err(Error::from)?; - Ok(manager) - } -} - -/// Part of builder pattern implementation for RedisBackend actor. -pub struct RedisBackendBuilder { - connection_info: String, -} - -impl Default for RedisBackendBuilder { - fn default() -> Self { - Self { - connection_info: "redis://127.0.0.1/".to_owned(), - } - } -} - -impl RedisBackendBuilder { - /// Set connection info (host, port, database, etc.) for RedisBackend actor. - pub fn server(mut self, connection_info: String) -> Self { - self.connection_info = connection_info; - self - } - - /// Create new instance of Redis backend with passed settings. - pub fn build(self) -> Result { - Ok(RedisBackend { - client: Client::open(self.connection_info)?, - connection: OnceCell::new(), - }) - } -} - -#[async_trait] -impl CacheBackend for RedisBackend { - async fn get(&self, key: String) -> BackendResult>> - where - T: CacheableResponse, - ::Cached: serde::de::DeserializeOwned, - { - let client = self.client.clone(); - async move { - let mut con = client.get_tokio_connection_manager().await.unwrap(); - let result: Option> = redis::cmd("GET") - .arg(key) - .query_async(&mut con) - .await - .map_err(Error::from) - .map_err(BackendError::from)?; - result - .map(|value| { - JsonSerializer::>::deserialize(value).map_err(BackendError::from) - }) - .transpose() - } - .await - } - - async fn delete(&self, key: String) -> BackendResult { - let mut con = self.connection().await?.clone(); - redis::cmd("DEL") - .arg(key) - .query_async(&mut con) - .await - .map(|res| { - if res > 0 { - DeleteStatus::Deleted(res) - } else { - DeleteStatus::Missing - } - }) - .map_err(Error::from) - .map_err(BackendError::from) - } - - async fn set( - &self, - key: String, - value: &CachedValue, - ttl: Option, - ) -> BackendResult<()> - where - T: CacheableResponse + Send, - T::Cached: serde::Serialize + Send + Sync, - { - let mut con = self.connection().await?.clone(); - let mut request = redis::cmd("SET"); - let serialized_value = - JsonSerializer::>::serialize(&value).map_err(BackendError::from)?; - request.arg(key).arg(serialized_value); - if let Some(ttl) = ttl { - request.arg("EX").arg(ttl); - }; - request - .query_async(&mut con) - .await - .map_err(Error::from) - .map_err(BackendError::from) - } - - async fn start(&self) -> BackendResult<()> { - self.connection().await?; - Ok(()) - } -} diff --git a/hitbox-redis/src/redis/builder.rs b/hitbox-redis/src/redis/builder.rs deleted file mode 100644 index e69de29..0000000 diff --git a/hitbox-redis/src/redis/mod.rs b/hitbox-redis/src/redis/mod.rs deleted file mode 100644 index 3bfa439..0000000 --- a/hitbox-redis/src/redis/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -mod backend; -mod builder;