mirror of
https://github.com/modrinth/code.git
synced 2026-08-26 17:44:50 +00:00
feat: use postcard for redis serde (#6956)
* redo error handling in xredis * give proper types to metadata fields * add round-trip tests * inline loader enum metadata fields * postcard roundtrips * prepare * bump redis key version * serde-binhum * clippy * fix * fix frontend checking existence of component fields rather than non-null-ness * prepare
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
use std::time::Duration;
|
||||
|
||||
use eyre::{Result, WrapErr};
|
||||
use futures::future::try_join_all;
|
||||
use prometheus::Registry;
|
||||
use redis::aio::ConnectionLike;
|
||||
@@ -7,7 +8,6 @@ use redis::cluster_read_routing::{
|
||||
RandomReplicaStrategy, RoundRobinReplicaStrategy,
|
||||
};
|
||||
use redis::cluster_routing::RoutingInfo;
|
||||
use thiserror::Error;
|
||||
use tracing::warn;
|
||||
|
||||
use crate::ReadReplicaStrategy;
|
||||
@@ -29,16 +29,6 @@ pub(crate) enum RedisBackend {
|
||||
ClusterMultiplexed(redis::cluster_async::ClusterConnection),
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum RedisBackendBuildError {
|
||||
#[error("failed to configure Redis client: {0}")]
|
||||
Redis(#[from] redis::RedisError),
|
||||
#[error("failed to build Redis pool: {0}")]
|
||||
PoolBuild(#[from] deadpool_redis::BuildError),
|
||||
#[error("failed to establish initial Redis pool connections: {0}")]
|
||||
Pool(#[from] deadpool_redis::PoolError),
|
||||
}
|
||||
|
||||
pub(crate) struct RedisConnection {
|
||||
inner: RedisConnectionInner,
|
||||
}
|
||||
@@ -58,9 +48,7 @@ pub(crate) trait RoutableConnection: ConnectionLike {
|
||||
}
|
||||
|
||||
impl RedisBackend {
|
||||
pub(crate) async fn new(
|
||||
config: &RedisConfig,
|
||||
) -> Result<Self, RedisBackendBuildError> {
|
||||
pub(crate) async fn new(config: &RedisConfig) -> Result<Self> {
|
||||
match config.backend() {
|
||||
RedisBackendConfig::StandalonePooled(pool_size) => {
|
||||
Self::standalone_pooled(config, pool_size).await
|
||||
@@ -77,21 +65,25 @@ impl RedisBackend {
|
||||
async fn standalone_pooled(
|
||||
config: &RedisConfig,
|
||||
pool_size: RedisPoolSize,
|
||||
) -> Result<Self, RedisBackendBuildError> {
|
||||
) -> Result<Self> {
|
||||
let connection_config = redis::AsyncConnectionConfig::new()
|
||||
.set_connection_timeout(None)
|
||||
.set_response_timeout(None);
|
||||
let manager = deadpool_redis::Manager::new_with_config(
|
||||
config.seed_urls()[0].clone(),
|
||||
connection_config,
|
||||
)?;
|
||||
)
|
||||
.wrap_err("configuring standalone Redis client")?;
|
||||
let pool = deadpool_redis::Pool::builder(manager)
|
||||
.max_size(pool_size.max())
|
||||
.wait_timeout(Some(Duration::from_millis(config.wait_timeout_ms())))
|
||||
.runtime(deadpool_redis::Runtime::Tokio1)
|
||||
.build()?;
|
||||
.build()
|
||||
.wrap_err("building standalone Redis pool")?;
|
||||
|
||||
warm_standalone_pool(&pool, pool_size.min()).await?;
|
||||
warm_standalone_pool(&pool, pool_size.min())
|
||||
.await
|
||||
.wrap_err("warming standalone Redis pool")?;
|
||||
retain_standalone_pool(pool.clone());
|
||||
|
||||
Ok(Self::StandalonePooled(pool))
|
||||
@@ -100,16 +92,18 @@ impl RedisBackend {
|
||||
async fn cluster_pooled(
|
||||
config: &RedisConfig,
|
||||
pool_size: RedisPoolSize,
|
||||
) -> Result<Self, RedisBackendBuildError> {
|
||||
) -> Result<Self> {
|
||||
let manager = deadpool_redis::cluster::Manager::new(
|
||||
config.seed_urls().to_vec(),
|
||||
false,
|
||||
)?;
|
||||
)
|
||||
.wrap_err("configuring clustered Redis client")?;
|
||||
let pool = deadpool_redis::cluster::Pool::builder(manager)
|
||||
.max_size(pool_size.max())
|
||||
.wait_timeout(Some(Duration::from_millis(config.wait_timeout_ms())))
|
||||
.runtime(deadpool_redis::Runtime::Tokio1)
|
||||
.build()?;
|
||||
.build()
|
||||
.wrap_err("building clustered Redis pool")?;
|
||||
|
||||
if config.read_replica_strategy() != ReadReplicaStrategy::Primary {
|
||||
warn!(
|
||||
@@ -117,15 +111,15 @@ impl RedisBackend {
|
||||
);
|
||||
}
|
||||
|
||||
warm_cluster_pool(&pool, pool_size.min()).await?;
|
||||
warm_cluster_pool(&pool, pool_size.min())
|
||||
.await
|
||||
.wrap_err("warming clustered Redis pool")?;
|
||||
retain_cluster_pool(pool.clone());
|
||||
|
||||
Ok(Self::ClusterPooled(pool))
|
||||
}
|
||||
|
||||
async fn cluster_multiplexed(
|
||||
config: &RedisConfig,
|
||||
) -> Result<Self, RedisBackendBuildError> {
|
||||
async fn cluster_multiplexed(config: &RedisConfig) -> Result<Self> {
|
||||
let mut builder = redis::cluster::ClusterClientBuilder::new(
|
||||
config.seed_urls().iter().map(String::as_str),
|
||||
);
|
||||
@@ -141,22 +135,31 @@ impl RedisBackend {
|
||||
}
|
||||
}
|
||||
|
||||
let client = builder.build()?;
|
||||
let connection = client.get_async_connection().await?;
|
||||
let client = builder
|
||||
.build()
|
||||
.wrap_err("building multiplexed Redis client")?;
|
||||
let connection = client
|
||||
.get_async_connection()
|
||||
.await
|
||||
.wrap_err("connecting multiplexed Redis client")?;
|
||||
|
||||
Ok(Self::ClusterMultiplexed(connection))
|
||||
}
|
||||
|
||||
pub(crate) async fn connect(
|
||||
&self,
|
||||
) -> Result<RedisConnection, deadpool_redis::PoolError> {
|
||||
pub(crate) async fn connect(&self) -> Result<RedisConnection> {
|
||||
let inner = match self {
|
||||
Self::StandalonePooled(pool) => {
|
||||
RedisConnectionInner::StandalonePooled(pool.get().await?)
|
||||
}
|
||||
Self::ClusterPooled(pool) => {
|
||||
RedisConnectionInner::ClusterPooled(pool.get().await?)
|
||||
RedisConnectionInner::StandalonePooled(
|
||||
pool.get()
|
||||
.await
|
||||
.wrap_err("fetching standalone Redis connection")?,
|
||||
)
|
||||
}
|
||||
Self::ClusterPooled(pool) => RedisConnectionInner::ClusterPooled(
|
||||
pool.get()
|
||||
.await
|
||||
.wrap_err("fetching clustered Redis connection")?,
|
||||
),
|
||||
Self::ClusterMultiplexed(connection) => {
|
||||
RedisConnectionInner::ClusterMultiplexed(connection.clone())
|
||||
}
|
||||
@@ -165,10 +168,7 @@ impl RedisBackend {
|
||||
Ok(RedisConnection { inner })
|
||||
}
|
||||
|
||||
pub(crate) fn register_metrics(
|
||||
&self,
|
||||
registry: &Registry,
|
||||
) -> Result<(), prometheus::Error> {
|
||||
pub(crate) fn register_metrics(&self, registry: &Registry) -> Result<()> {
|
||||
register_command_pool_metrics(registry, self.clone())
|
||||
}
|
||||
}
|
||||
@@ -282,8 +282,10 @@ impl RoutableConnection for RedisConnection {
|
||||
async fn warm_standalone_pool(
|
||||
pool: &deadpool_redis::Pool,
|
||||
min: usize,
|
||||
) -> Result<(), deadpool_redis::PoolError> {
|
||||
let connections = try_join_all((0..min).map(|_| pool.get())).await?;
|
||||
) -> Result<()> {
|
||||
let connections = try_join_all((0..min).map(|_| pool.get()))
|
||||
.await
|
||||
.wrap_err("fetching initial standalone Redis connections")?;
|
||||
drop(connections);
|
||||
Ok(())
|
||||
}
|
||||
@@ -291,8 +293,10 @@ async fn warm_standalone_pool(
|
||||
async fn warm_cluster_pool(
|
||||
pool: &deadpool_redis::cluster::Pool,
|
||||
min: usize,
|
||||
) -> Result<(), deadpool_redis::PoolError> {
|
||||
let connections = try_join_all((0..min).map(|_| pool.get())).await?;
|
||||
) -> Result<()> {
|
||||
let connections = try_join_all((0..min).map(|_| pool.get()))
|
||||
.await
|
||||
.wrap_err("fetching initial clustered Redis connections")?;
|
||||
drop(connections);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user