diff --git a/editoast/Cargo.lock b/editoast/Cargo.lock index bf5ebee07cf..53346642ba9 100644 --- a/editoast/Cargo.lock +++ b/editoast/Cargo.lock @@ -630,15 +630,6 @@ dependencies = [ "tracing-opentelemetry-instrumentation-sdk", ] -[[package]] -name = "backon" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba5289ec98f68f28dd809fd601059e6aa908bb8f6108620930828283d4ee23d7" -dependencies = [ - "fastrand 2.3.0", -] - [[package]] name = "backtrace" version = "0.3.74" @@ -959,12 +950,6 @@ dependencies = [ "libc", ] -[[package]] -name = "crc16" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "338089f42c427b86394a5ee60ff321da23a5c89c9d89514c829687b26359fcff" - [[package]] name = "crc32fast" version = "1.4.2" @@ -1077,6 +1062,16 @@ dependencies = [ "tokio", ] +[[package]] +name = "deadpool-redis" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfae6799b68a735270e4344ee3e834365f707c72da09c9a8bb89b45cc3351395" +dependencies = [ + "deadpool", + "redis", +] + [[package]] name = "deadpool-runtime" version = "0.1.4" @@ -1295,6 +1290,7 @@ dependencies = [ "colored", "dashmap", "deadpool", + "deadpool-redis", "derivative", "diesel", "diesel-async", @@ -1332,7 +1328,6 @@ dependencies = [ "pretty_assertions", "rand 0.8.5", "rangemap", - "redis", "regex", "reqwest", "rstest", @@ -3744,28 +3739,22 @@ dependencies = [ [[package]] name = "redis" -version = "0.28.2" +version = "0.27.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e37ec3fd44bea2ec947ba6cc7634d7999a6590aca7c35827c250bc0de502bda6" +checksum = "09d8f99a4090c89cc489a94833c901ead69bfbf3877b4867d5482e321ee875bc" dependencies = [ "arc-swap", - "backon", + "async-trait", "bytes", "combine", - "crc16", - "futures-channel", - "futures-sink", "futures-util", + "itertools", "itoa", - "log", - "native-tls", "num-bigint", "percent-encoding", "pin-project-lite", - "rand 0.8.5", "ryu", "tokio", - "tokio-native-tls", "tokio-util", "url", ] diff --git a/editoast/Cargo.toml b/editoast/Cargo.toml index e9d92a76cb8..ccf7e1fd49d 100644 --- a/editoast/Cargo.toml +++ b/editoast/Cargo.toml @@ -118,6 +118,7 @@ deadpool = { version = "0.12.2", features = [ "rt_tokio_1", "unmanaged", ] } +deadpool-redis = "0.18.0" derivative.workspace = true diesel.workspace = true diesel-async = { workspace = true } @@ -168,12 +169,6 @@ pathfinding = "4.14.0" postgis_diesel.workspace = true rand.workspace = true rangemap.workspace = true -redis = { version = "0.28", default-features = false, features = [ - "cluster-async", - "connection-manager", - "tokio-comp", - "tokio-native-tls-comp", -] } regex.workspace = true reqwest.workspace = true serde = { workspace = true, features = ["derive"] } diff --git a/editoast/src/error.rs b/editoast/src/error.rs index 2f8a2e4483c..96b5e4342e6 100644 --- a/editoast/src/error.rs +++ b/editoast/src/error.rs @@ -2,11 +2,12 @@ use axum::http::StatusCode; use axum::response::{IntoResponse, Response}; use axum::Json; use colored::Colorize; +use deadpool_redis::redis::RedisError; +use deadpool_redis::PoolError; use diesel::result::Error as DieselError; use editoast_models::db_connection_pool::DatabasePoolBuildError; use editoast_models::db_connection_pool::DatabasePoolError; use editoast_models::DatabaseError; -use redis::RedisError; use serde::Deserialize; use serde::Serialize; use serde_json::{json, Value}; @@ -171,7 +172,16 @@ impl EditoastError for RedisError { } } -// Handle all json errors +/// Handle all valkey pool errors +impl EditoastError for PoolError { + fn get_status(&self) -> StatusCode { + StatusCode::INTERNAL_SERVER_ERROR + } + + fn get_type(&self) -> &str { + "editoast:ValkeyPoolError" + } +} /// Handle all json errors impl EditoastError for ValidationErrors { diff --git a/editoast/src/map/mod.rs b/editoast/src/map/mod.rs index 67592de9cf1..bf4b7146124 100644 --- a/editoast/src/map/mod.rs +++ b/editoast/src/map/mod.rs @@ -1,10 +1,10 @@ mod layer_cache; mod layers; +use deadpool_redis::redis::AsyncCommands; pub use layers::Layer; pub use layers::MapLayers; pub use layers::View; -use redis::AsyncCommands; pub use self::layer_cache::get_cache_tile_key; pub use self::layer_cache::get_layer_cache_prefix; diff --git a/editoast/src/valkey_utils.rs b/editoast/src/valkey_utils.rs index 454cb59256b..9e1ebbe1a3a 100644 --- a/editoast/src/valkey_utils.rs +++ b/editoast/src/valkey_utils.rs @@ -1,17 +1,23 @@ use std::fmt::Debug; +use deadpool_redis::redis::aio::ConnectionLike; +use deadpool_redis::redis::cmd; +use deadpool_redis::redis::Arg; +use deadpool_redis::redis::AsyncCommands; +use deadpool_redis::redis::Cmd; +use deadpool_redis::redis::ErrorKind; +use deadpool_redis::redis::Pipeline; +use deadpool_redis::redis::RedisError; +use deadpool_redis::redis::RedisFuture; +use deadpool_redis::redis::ToRedisArgs; +use deadpool_redis::redis::Value; +use deadpool_redis::Config; +use deadpool_redis::Connection; +use deadpool_redis::Pool; +use deadpool_redis::PoolError; +use deadpool_redis::Runtime; use futures::future; use futures::FutureExt; -use redis::aio::ConnectionLike; -use redis::aio::ConnectionManager; -use redis::cmd; -use redis::AsyncCommands; -use redis::Client; -use redis::ErrorKind; -use redis::RedisError; -use redis::RedisFuture; -use redis::RedisResult; -use redis::ToRedisArgs; use serde::de::DeserializeOwned; use serde::Serialize; use tracing::{debug, span, trace, Level}; @@ -20,41 +26,41 @@ use url::Url; use crate::error::Result; pub enum ValkeyConnection { - Tokio(ConnectionManager), + Tokio(Connection), NoCache, } -fn no_cache_cmd_handler(cmd: &redis::Cmd) -> std::result::Result { - let cmd_name = cmd.args_iter().next().ok_or(( - redis::ErrorKind::ClientError, - "missing a command instruction", - ))?; +fn no_cache_cmd_handler(cmd: &Cmd) -> std::result::Result { + let cmd_name = cmd + .args_iter() + .next() + .ok_or((ErrorKind::ClientError, "missing a command instruction"))?; let nb_keys = cmd.args_iter().skip(1).count(); match cmd_name { - redis::Arg::Simple(cmd_name_bytes) + Arg::Simple(cmd_name_bytes) if cmd_name_bytes == "MGET".as_bytes() || cmd_name_bytes == "MSET".as_bytes() || nb_keys > 1 => { - Ok(redis::Value::Array(vec![redis::Value::Nil; nb_keys])) + Ok(Value::Array(vec![Value::Nil; nb_keys])) }, - redis::Arg::Simple(_) + Arg::Simple(_) if nb_keys == 1 => { - Ok(redis::Value::Nil) + Ok(Value::Nil) }, - redis::Arg::Simple(cmd_name_bytes) if cmd_name_bytes == "PING".as_bytes() => Ok(redis::Value::SimpleString("PONG".to_string())), - redis::Arg::Simple(cmd_name_bytes) => unimplemented!( + Arg::Simple(cmd_name_bytes) if cmd_name_bytes == "PING".as_bytes() => Ok(Value::SimpleString("PONG".to_string())), + Arg::Simple(cmd_name_bytes) => unimplemented!( "valkey command '{}' is not supported by editoast::valkey_utils::ValkeyConnection with '--no-cache'", String::from_utf8(cmd_name_bytes.to_vec())? ), - redis::Arg::Cursor => unimplemented!( + Arg::Cursor => unimplemented!( "valkey cursor mode is not supported by editoast::valkey_utils::ValkeyConnection with '--no-cache'" ), } } impl ConnectionLike for ValkeyConnection { - fn req_packed_command<'a>(&'a mut self, cmd: &'a redis::Cmd) -> RedisFuture<'a, redis::Value> { + fn req_packed_command<'a>(&'a mut self, cmd: &'a Cmd) -> RedisFuture<'a, Value> { match self { ValkeyConnection::Tokio(connection) => connection.req_packed_command(cmd), ValkeyConnection::NoCache => future::ready(no_cache_cmd_handler(cmd)).boxed(), @@ -63,10 +69,10 @@ impl ConnectionLike for ValkeyConnection { fn req_packed_commands<'a>( &'a mut self, - cmd: &'a redis::Pipeline, + cmd: &'a Pipeline, offset: usize, count: usize, - ) -> RedisFuture<'a, Vec> { + ) -> RedisFuture<'a, Vec> { match self { ValkeyConnection::Tokio(connection) => { connection.req_packed_commands(cmd, offset, count) @@ -77,7 +83,7 @@ impl ConnectionLike for ValkeyConnection { .skip(offset) .take(count) .map(no_cache_cmd_handler) - .collect::>(); + .collect::>(); future::ready(responses).boxed() } } @@ -259,7 +265,7 @@ impl ValkeyConnection { #[derive(Clone)] pub enum ValkeyClient { - Tokio(Client), + Tokio(Pool), /// This doesn't cache anything. It has no backend. NoCache, } @@ -277,21 +283,21 @@ impl ValkeyClient { return Ok(ValkeyClient::NoCache); } Ok(ValkeyClient::Tokio( - redis::Client::open(valkey_config.valkey_url).unwrap(), + Config::from_url(valkey_config.valkey_url) + .create_pool(Some(Runtime::Tokio1)) + .unwrap(), )) } - pub async fn get_connection(&self) -> RedisResult { + pub async fn get_connection(&self) -> std::result::Result { match self { - ValkeyClient::Tokio(client) => Ok(ValkeyConnection::Tokio( - client.get_connection_manager().await?, - )), + ValkeyClient::Tokio(pool) => Ok(ValkeyConnection::Tokio(pool.get().await?)), ValkeyClient::NoCache => Ok(ValkeyConnection::NoCache), } } #[tracing::instrument(skip_all)] - pub async fn ping_valkey(&self) -> RedisResult<()> { + pub async fn ping_valkey(&self) -> anyhow::Result<()> { let mut conn = self.get_connection().await?; cmd("PING").query_async::<()>(&mut conn).await?; trace!("Valkey ping successful"); diff --git a/editoast/src/views/layers.rs b/editoast/src/views/layers.rs index 3ba79b3b203..49244fcbfd4 100644 --- a/editoast/src/views/layers.rs +++ b/editoast/src/views/layers.rs @@ -7,9 +7,9 @@ use axum::extract::State; use axum::http::header::CONTENT_TYPE; use axum::response::IntoResponse; use axum::Extension; +use deadpool_redis::redis::AsyncCommands; use editoast_authz::BuiltinRole; use editoast_derive::EditoastError; -use redis::AsyncCommands; use serde::Deserialize; use serde::Serialize; use thiserror::Error; diff --git a/editoast/src/views/mod.rs b/editoast/src/views/mod.rs index 71b696023ae..25f4f61b9e6 100644 --- a/editoast/src/views/mod.rs +++ b/editoast/src/views/mod.rs @@ -266,7 +266,7 @@ pub enum AppHealthError { #[error(transparent)] Database(#[from] editoast_models::db_connection_pool::PingError), #[error(transparent)] - Valkey(#[from] redis::RedisError), + Valkey(#[from] anyhow::Error), #[error(transparent)] Core(#[from] CoreError), }