diff --git a/Cargo.lock b/Cargo.lock index d379d08..3cb48f4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,6 +8,12 @@ version = "1.0.100" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" +[[package]] +name = "arcstr" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03918c3dbd7701a85c6b9887732e2921175f26c350b4563841d0958c21d57e6d" + [[package]] name = "atomic-waker" version = "1.1.2" @@ -119,7 +125,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" dependencies = [ "bytes", + "futures-core", "memchr", + "pin-project-lite", + "tokio", + "tokio-util", ] [[package]] @@ -138,6 +148,12 @@ version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" +[[package]] +name = "crc16" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "338089f42c427b86394a5ee60ff321da23a5c89c9d89514c829687b26359fcff" + [[package]] name = "displaydoc" version = "0.2.5" @@ -277,6 +293,7 @@ checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" dependencies = [ "futures-core", "futures-macro", + "futures-sink", "futures-task", "pin-project-lite", "pin-utils", @@ -850,6 +867,15 @@ dependencies = [ "zerovec", ] +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + [[package]] name = "proc-macro2" version = "1.0.103" @@ -874,20 +900,60 @@ version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" +[[package]] +name = "rand" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" +dependencies = [ + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +dependencies = [ + "getrandom 0.3.4", +] + [[package]] name = "redis" -version = "0.32.7" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "014cc767fefab6a3e798ca45112bccad9c6e0e218fbd49720042716c73cfef44" +checksum = "47ba378d39b8053bffbfc2750220f5a24a06189b5129523d5db01618774e0239" dependencies = [ + "arcstr", + "bytes", + "cfg-if", "combine", + "crc16", + "futures-util", "itoa", + "log", "num-bigint", "percent-encoding", + "pin-project-lite", + "rand", "ryu", "sha1_smol", "socket2", + "tokio", + "tokio-util", "url", + "xxhash-rust", ] [[package]] @@ -1808,6 +1874,12 @@ version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" +[[package]] +name = "xxhash-rust" +version = "0.8.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdd20c5420375476fbd4394763288da7eb0cc0b8c11deed431a91562af7335d3" + [[package]] name = "yoke" version = "0.8.1" @@ -1831,6 +1903,26 @@ dependencies = [ "synstructure", ] +[[package]] +name = "zerocopy" +version = "0.8.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd74ec98b9250adb3ca554bdde269adf631549f51d8a8f8f0a10b50f1cb298c3" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8a8d209fdf45cf5138cbb5a506f6b52522a25afccc534d1475dad8e31105c6a" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "zerofrom" version = "0.1.6" diff --git a/Cargo.toml b/Cargo.toml index 593e84f..e5418d6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,7 +9,7 @@ futures-util = "0.3.31" tokio = { version = "1.48.0", features = ["full"] } tower-http = { version = "0.5", features = ["cors", "trace", "limit"] } -redis = "0.32.7" +redis = { version = "1.0.0", features = ["cluster-async", "tokio-comp"] } tracing = "0.1.41" tracing-subscriber = "0.3" diff --git a/src/config.rs b/src/config.rs index 28da527..977c0b7 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,11 +1,15 @@ use serde::Deserialize; -use crate::middleware::{Server, ServerClients}; +use crate::{ + db::{self, RedisClient}, + middleware::{Server, ServerClients}, +}; #[derive(Deserialize, Debug)] pub struct SystemConfig { pub available_servers: String, // TODO: This should be hosted in redis pub port: u16, + pub redis_url: String, } impl SystemConfig { @@ -17,23 +21,28 @@ impl SystemConfig { } } -#[derive(Clone, Debug)] +#[derive(Clone)] pub struct State { pub available_servers: ServerClients, + pub redis_conn: RedisClient, } impl State { - pub fn new(config: &SystemConfig) -> anyhow::Result { + pub async fn new(config: &SystemConfig) -> Result> { let servers = config.available_servers.split(',').collect::>(); let available_servers: Vec = servers .clone() .into_iter() .map(Server::new) - .collect::>>()?; + .collect::, _>>()?; + + let redis_conn = + db::RedisClient::init_redis(&config.redis_url, available_servers.clone()).await?; Ok(State { available_servers: ServerClients::new(available_servers), + redis_conn, }) } } diff --git a/src/db/mod.rs b/src/db/mod.rs new file mode 100644 index 0000000..c94d904 --- /dev/null +++ b/src/db/mod.rs @@ -0,0 +1,3 @@ +mod redis; + +pub use redis::RedisClient; diff --git a/src/db/redis.rs b/src/db/redis.rs new file mode 100644 index 0000000..1b2ac22 --- /dev/null +++ b/src/db/redis.rs @@ -0,0 +1,38 @@ +use redis::{AsyncTypedCommands as _, cluster::ClusterClient, cluster_async::ClusterConnection}; + +use crate::{error::Error, middleware::Server}; + +#[derive(Clone)] +pub struct RedisClient(ClusterConnection); + +impl RedisClient { + pub async fn init_redis( + redis_url: &str, + available_servers: Vec, + ) -> Result> { + let nodes = vec![redis_url]; + let client = ClusterClient::new(nodes)?; + let mut connection = client.get_async_connection().await?; + + // Preload server urls into Redis + for (server_index, server) in available_servers.clone().into_iter().enumerate() { + connection + .set(format!("server_{}", server_index), server.url.as_str()) + .await?; + } + + Ok(Self(connection)) + } + + pub async fn set(&mut self, key: &str, value: &str) -> Result<(), Error> { + Ok(self.0.set(key, value).await?) + } + + pub async fn get(&mut self, key: &str) -> Result, Error> { + Ok(self.0.get(key).await?) + } + + pub async fn delete(&mut self, key: &str) -> Result { + Ok(self.0.del(key).await?) + } +} diff --git a/src/error.rs b/src/error.rs index bfbe252..dc84168 100644 --- a/src/error.rs +++ b/src/error.rs @@ -12,6 +12,8 @@ pub enum Error { Unauthorized, #[error("Other: {0}")] Other(#[from] anyhow::Error), + #[error("Redis Error: {0}")] + RedisError(#[from] redis::RedisError), #[error("Method Not Allowed")] MethodNotAllowed, #[error("Invalid URL")] @@ -24,7 +26,10 @@ impl IntoResponse for Error { fn into_response(self) -> Response { match self { Error::NotFound => (StatusCode::NOT_FOUND, self).into_response(), - Error::InternalServerError | Error::Other(_) | Error::InvalidResponse => { + Error::InternalServerError + | Error::Other(_) + | Error::InvalidResponse + | Error::RedisError(_) => { (StatusCode::INTERNAL_SERVER_ERROR, "Internal Server Error").into_response() } Error::Unauthorized => (StatusCode::UNAUTHORIZED, self).into_response(), diff --git a/src/main.rs b/src/main.rs index 24620c4..a1400f4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -18,10 +18,11 @@ use crate::{ }; pub mod config; +pub mod db; pub mod error; -pub mod middleware; -pub mod servers; -pub mod services; +mod middleware; +mod servers; +mod services; #[tokio::main] async fn main() -> Result<(), Box> { @@ -32,7 +33,7 @@ async fn main() -> Result<(), Box> { let config = SystemConfig::from_env()?; - let state = State::new(&config)?; + let state = State::new(&config).await?; let server = Router::new() .route("/status", get(status))