Files
modrinth/packages/xredis/src/metrics.rs
T
aecsocket 5148e8ec35 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
2026-08-05 10:36:05 +00:00

176 lines
4.7 KiB
Rust

use std::time::Duration;
use eyre::{Result, WrapErr};
use prometheus::{IntGauge, Registry};
const METRICS_UPDATE_INTERVAL: Duration = Duration::from_secs(5);
#[derive(Debug, Clone, Copy)]
pub(super) struct LogicalPoolStatus {
max_size: usize,
size: usize,
available: usize,
waiting: usize,
}
impl LogicalPoolStatus {
pub(super) fn from_deadpool(status: deadpool_redis::Status) -> Self {
Self {
max_size: status.max_size,
size: status.size,
available: status.available,
waiting: status.waiting,
}
}
pub(super) fn shared_multiplexed() -> Self {
Self {
max_size: 1,
size: 1,
available: 1,
waiting: 0,
}
}
}
pub(super) trait LogicalPoolStatusProvider:
Clone + Send + 'static
{
fn logical_pool_status(&self) -> LogicalPoolStatus;
}
#[derive(Debug, Clone, Copy)]
enum RedisPoolMetricsKind {
Command,
Blocking,
}
impl RedisPoolMetricsKind {
fn metric_prefix(self) -> &'static str {
match self {
Self::Command => "labrinth_redis_pool",
Self::Blocking => "labrinth_redis_blocking_pool",
}
}
fn description(self) -> &'static str {
match self {
Self::Command => "Redis command pool",
Self::Blocking => "Redis blocking-command pool",
}
}
}
#[derive(Clone)]
struct RedisPoolMetrics {
max_size: IntGauge,
size: IntGauge,
available: IntGauge,
waiting: IntGauge,
}
impl RedisPoolMetrics {
fn register(
registry: &Registry,
kind: RedisPoolMetricsKind,
) -> Result<Self> {
let prefix = kind.metric_prefix();
let description = kind.description();
let max_size = IntGauge::new(
format!("{prefix}_max_size"),
format!(
"Maximum logical connection count for the {description}; clustered logical connections may own multiple physical sockets"
),
)
.wrap_err("creating Redis pool maximum size metric")?;
let size = IntGauge::new(
format!("{prefix}_size"),
format!(
"Current logical connection count for the {description}; clustered logical connections may own multiple physical sockets"
),
)
.wrap_err("creating Redis pool size metric")?;
let available = IntGauge::new(
format!("{prefix}_available"),
format!("Available logical connections in the {description}"),
)
.wrap_err("creating Redis pool availability metric")?;
let waiting = IntGauge::new(
format!("{prefix}_waiting"),
format!(
"Number of futures waiting for a logical connection from the {description}"
),
)
.wrap_err("creating Redis pool waiters metric")?;
registry
.register(Box::new(max_size.clone()))
.wrap_err("registering Redis pool maximum size metric")?;
registry
.register(Box::new(size.clone()))
.wrap_err("registering Redis pool size metric")?;
registry
.register(Box::new(available.clone()))
.wrap_err("registering Redis pool availability metric")?;
registry
.register(Box::new(waiting.clone()))
.wrap_err("registering Redis pool waiters metric")?;
Ok(Self {
max_size,
size,
available,
waiting,
})
}
fn set(&self, status: LogicalPoolStatus) {
self.max_size.set(status.max_size as i64);
self.size.set(status.size as i64);
self.available.set(status.available as i64);
self.waiting.set(status.waiting as i64);
}
}
pub(super) fn register_command_pool_metrics<P>(
registry: &Registry,
provider: P,
) -> Result<()>
where
P: LogicalPoolStatusProvider,
{
register_pool_metrics(registry, RedisPoolMetricsKind::Command, provider)
}
pub(super) fn register_blocking_pool_metrics<P>(
registry: &Registry,
provider: P,
) -> Result<()>
where
P: LogicalPoolStatusProvider,
{
register_pool_metrics(registry, RedisPoolMetricsKind::Blocking, provider)
}
fn register_pool_metrics<P>(
registry: &Registry,
kind: RedisPoolMetricsKind,
provider: P,
) -> Result<()>
where
P: LogicalPoolStatusProvider,
{
let metrics = RedisPoolMetrics::register(registry, kind)
.wrap_err("registering Redis pool metrics")?;
metrics.set(provider.logical_pool_status());
tokio::spawn(async move {
loop {
tokio::time::sleep(METRICS_UPDATE_INTERVAL).await;
metrics.set(provider.logical_pool_status());
}
});
Ok(())
}