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:
aecsocket
2026-08-05 10:36:05 +00:00
committed by GitHub
parent cc53d9a6f8
commit 5148e8ec35
70 changed files with 1713 additions and 646 deletions
+46 -42
View File
@@ -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(())
}