diff --git a/Cargo.lock b/Cargo.lock index 85117faab..8245cfff0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2247,30 +2247,28 @@ checksum = "be1e0bca6c3637f992fc1cc7cbc52a78c1ef6db076dbf1059c4323d6a2048376" [[package]] name = "deadpool" version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0be2b1d1d6ec8d846f05e137292d0b89133caf95ef33695424c09568bdd39b1b" +source = "git+https://github.com/modrinth/deadpool?rev=db5fb00b036ecc8fe5f18853c559b745ffe47bde#db5fb00b036ecc8fe5f18853c559b745ffe47bde" dependencies = [ "deadpool-runtime", - "lazy_static", "num_cpus", "tokio", + "tracing", ] [[package]] name = "deadpool-redis" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0965b977f1244bc3783bb27cd79cfcff335a8341da18f79232d00504b18eb1a" +version = "0.22.1" +source = "git+https://github.com/modrinth/deadpool?rev=db5fb00b036ecc8fe5f18853c559b745ffe47bde#db5fb00b036ecc8fe5f18853c559b745ffe47bde" dependencies = [ "deadpool", "redis", + "tracing", ] [[package]] name = "deadpool-runtime" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "092966b41edc516079bdf31ec78a2e0588d1d0c08f78b91d8307215928642b2b" +version = "0.1.5" +source = "git+https://github.com/modrinth/deadpool?rev=db5fb00b036ecc8fe5f18853c559b745ffe47bde#db5fb00b036ecc8fe5f18853c559b745ffe47bde" dependencies = [ "tokio", ] diff --git a/Cargo.toml b/Cargo.toml index 5b52543f6..3b1e5553e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -60,7 +60,7 @@ const_format = "0.2.34" daedalus = { path = "packages/daedalus" } dashmap = "6.1.0" data-url = "0.3.2" -deadpool-redis = "0.22.0" +deadpool-redis = { version = "0.22.1", git = "https://github.com/modrinth/deadpool", rev = "db5fb00b036ecc8fe5f18853c559b745ffe47bde" } derive_more = "2.0.1" directories = "6.0.0" dirs = "6.0.0" diff --git a/_typos.toml b/_typos.toml index 5961cd5dc..adee7387c 100644 --- a/_typos.toml +++ b/_typos.toml @@ -17,3 +17,7 @@ extend-exclude = [ tou = "tou" # Google Ad Manager gam = "gam" +# short for "constants" +consts = "consts" +# short for "Copy" +Cpy = "Cpy" diff --git a/apps/labrinth/.env.docker-compose b/apps/labrinth/.env.docker-compose index 311e77c96..49ddff31c 100644 --- a/apps/labrinth/.env.docker-compose +++ b/apps/labrinth/.env.docker-compose @@ -19,6 +19,7 @@ MEILISEARCH_WRITE_ADDRS=http://localhost:7700 MEILISEARCH_KEY=modrinth REDIS_URL=redis://labrinth-redis +REDIS_MIN_CONNECTIONS=0 REDIS_MAX_CONNECTIONS=10000 BIND_ADDR=0.0.0.0:8000 diff --git a/apps/labrinth/.env.local b/apps/labrinth/.env.local index 9d32888c7..98b92c184 100644 --- a/apps/labrinth/.env.local +++ b/apps/labrinth/.env.local @@ -27,6 +27,7 @@ SEARCH_OPERATION_TIMEOUT=300000 MEILISEARCH_KEY=modrinth REDIS_URL=redis://localhost +REDIS_MIN_CONNECTIONS=0 REDIS_MAX_CONNECTIONS=10000 # Must bind to broadcast, not localhost, because some diff --git a/apps/labrinth/src/database/redis/mod.rs b/apps/labrinth/src/database/redis/mod.rs index 1a3d02a47..bd84f6716 100644 --- a/apps/labrinth/src/database/redis/mod.rs +++ b/apps/labrinth/src/database/redis/mod.rs @@ -3,6 +3,7 @@ use ariadne::ids::base62_impl::{parse_base62, to_base62}; use chrono::{TimeZone, Utc}; use dashmap::DashMap; use deadpool_redis::{Config, Runtime}; +use futures::TryStreamExt; use futures::future::Either; use futures::stream::{FuturesUnordered, StreamExt}; use prometheus::{IntGauge, Registry}; @@ -14,7 +15,7 @@ use std::fmt::{Debug, Display}; use std::future::Future; use std::hash::Hash; use std::time::Duration; -use tracing::{Instrument, info_span}; +use tracing::{Instrument, info, info_span}; use util::{cmd, redis_pipe}; pub mod util; @@ -25,7 +26,7 @@ const ACTUAL_EXPIRY: i64 = 60 * 30; // 30 minutes #[derive(Clone)] pub struct RedisPool { pub url: String, - pub pool: util::InstrumentedPool, + pub pool: deadpool_redis::Pool, cache_list: DashMap, meta_namespace: String, } @@ -69,11 +70,35 @@ impl RedisPool { let pool = RedisPool { url, - pool: util::InstrumentedPool::new(pool), + pool, cache_list: DashMap::with_capacity(2048), meta_namespace: meta_namespace.unwrap_or("".to_string()), }; + let redis_min_connections = dotenvy::var("REDIS_MIN_CONNECTIONS") + .ok() + .and_then(|x| x.parse::().ok()) + .unwrap_or(0); + let spawn_min_connections = (0..redis_min_connections) + .map(|_| { + let pool = pool.clone(); + tokio::spawn(async move { pool.pool.get().await }) + }) + .collect::>(); + tokio::spawn({ + let pool = pool.clone(); + async move { + // collect the connections into a buffer while we're spawning them, + // to make sure that we're not `get`ing any connections we previously took + let _connections = + spawn_min_connections.try_collect::>().await; + info!( + pool_status = ?pool.pool.status(), + "Finished getting {redis_min_connections} initial Redis connections" + ); + } + }); + let interval = Duration::from_secs(30); let max_age = Duration::from_secs(5 * 60); // 5 minutes let pool_ref = pool.clone(); diff --git a/apps/labrinth/src/database/redis/util.rs b/apps/labrinth/src/database/redis/util.rs index 7e7ef3be7..899370811 100644 --- a/apps/labrinth/src/database/redis/util.rs +++ b/apps/labrinth/src/database/redis/util.rs @@ -2,7 +2,6 @@ use std::fmt::Debug; use std::sync::Arc; use std::sync::atomic::{AtomicBool, Ordering}; -use deadpool_redis::PoolError; use derive_more::{Deref, DerefMut}; use redis::{FromRedisValue, RedisResult, ToRedisArgs}; use tokio::sync::Notify; @@ -11,24 +10,6 @@ use tracing::{Instrument, info_span}; use crate::database::models::DatabaseError; -#[derive(Debug, Clone, Deref, DerefMut)] -pub struct InstrumentedPool { - inner: deadpool_redis::Pool, -} - -impl InstrumentedPool { - pub fn new(inner: deadpool_redis::Pool) -> Self { - Self { inner } - } - - pub async fn get(&self) -> Result { - self.inner - .get() - .instrument(info_span!("get redis connection")) - .await - } -} - pub fn redis_pipe() -> InstrumentedPipeline { InstrumentedPipeline { inner: redis::pipe(), @@ -55,7 +36,7 @@ impl InstrumentedPipeline { ) -> RedisResult { self.inner .query_async(con) - .instrument(info_span!("execute redis pipeline")) + .instrument(info_span!("pipeline.query_async")) .await } } @@ -88,7 +69,7 @@ impl InstrumentedCmd { con: &mut impl redis::aio::ConnectionLike, ) -> RedisResult { let span = info_span!( - "query_async", + "cmd.query_async", // db.system.name = "redis", db.operation.name = self.name,