redo error handling in xredis

This commit is contained in:
aecsocket
2026-08-04 17:20:20 +01:00
committed by Calum H.
parent 99377f8436
commit a414ff0853
20 changed files with 408 additions and 327 deletions
+3 -1
View File
@@ -12,8 +12,10 @@
- no trailing punctuation
- wrap code items e.g. type names in backticks
- Prefer `wrap_internal_err`, `wrap_request_err` when attaching context to an existing error (like Anyhow `context` or Eyre `wrap_err`)
- Prefer importing `eyre::Result` and using `Result<T>` instead of `eyre::Result<T>`
- Prefer `eyre::Ok(value)` instead of `Ok::<_, eyre::Report>(value)` when an explicit Eyre result type is needed
- All operations should ideally have some context attached
- Database operations can have a message like `.wrap_internal_err("failed to fetch XYZ")`
- Database operations can have a message like `.wrap_internal_err("fetching XYZ")`
- You can perform real-time queries against the databases in the Docker Compose
- `docker exec labrinth-postgres psql -c "select 1"`
- `docker exec labrinth-redis redis-cli flushall`
-6
View File
@@ -59,12 +59,6 @@ pub enum AuthenticationError {
Url,
}
impl From<xredis::Error> for AuthenticationError {
fn from(error: xredis::Error) -> Self {
Self::Database(error.into())
}
}
impl actix_web::ResponseError for AuthenticationError {
fn status_code(&self) -> StatusCode {
match self {
-2
View File
@@ -77,8 +77,6 @@ pub enum DatabaseError {
SerdeCacheError(#[from] serde_json::Error),
#[error("error while encoding or decoding the cache: {0}")]
PostcardCacheError(#[from] postcard::Error),
#[error(transparent)]
Redis(#[from] xredis::Error),
#[error("Schema error: {0}")]
SchemaError(String),
}
@@ -1,6 +1,7 @@
use crate::database::models::DatabaseError;
use crate::models::v3::notifications::{NotificationChannel, NotificationType};
use crate::routes::ApiError;
use crate::util::error::Context;
use serde::{Deserialize, Serialize};
use xredis::RedisPool;
@@ -123,12 +124,16 @@ where
html: String,
}
let mut redis_conn = redis.connect().await?;
let mut redis_conn = redis.connect().await.wrap_internal_err(
"connecting to redis for dynamic notification html",
)?;
let redis_key = redis_conn
.key()
.metadata(TEMPLATES_DYNAMIC_HTML_NAMESPACE, key);
if let Some(body) =
redis_conn.get_deserialized::<HtmlBody>(&redis_key).await?
if let Some(body) = redis_conn
.get_deserialized::<HtmlBody>(&redis_key)
.await
.wrap_internal_err("fetching dynamic notification html from redis")?
{
return Ok(body.html);
}
@@ -136,14 +141,17 @@ where
drop(redis_conn);
let cached = HtmlBody { html: get().await? };
let mut redis_conn = redis.connect().await?;
let mut redis_conn = redis.connect().await.wrap_internal_err(
"connecting to redis for dynamic notification html",
)?;
let redis_key = redis_conn
.key()
.metadata(TEMPLATES_DYNAMIC_HTML_NAMESPACE, key);
redis_conn
.set_serialized(&redis_key, &cached, Some(HTML_DATA_CACHE_EXPIRY))
.await?;
.await
.wrap_internal_err("writing dynamic notification html to redis")?;
Ok(cached.html)
}
@@ -946,7 +946,7 @@ impl DBProject {
},
)
.await
.wrap_internal_err("failed to fetch cached projects")?;
.wrap_internal_err("fetching cached projects")?;
Ok(val)
}
+4 -2
View File
@@ -1,6 +1,7 @@
use std::sync::Arc;
use crate::env::ENV;
use eyre::{Result, WrapErr};
struct RedisConfig {
inner: xredis::RedisConfig,
@@ -8,7 +9,7 @@ struct RedisConfig {
}
impl RedisConfig {
fn from_env() -> Result<Self, xredis::RedisConfigError> {
fn from_env() -> Result<Self> {
let inner = xredis::RedisConfig::new(
ENV.REDIS_TOPOLOGY,
ENV.REDIS_CONNECTION_TYPE,
@@ -25,7 +26,8 @@ impl RedisConfig {
(ENV.REDIS_BLOCKING_MAX_CONNECTIONS as usize, 0),
ENV.REDIS_CACHE_LOCKING_STRATEGY,
ENV.REDIS_READ_REPLICA_STRATEGY,
)?;
)
.wrap_err("loading Redis configuration from environment")?;
let cache_settings = xredis::CacheSettings {
default_expiry: ENV.REDIS_DEFAULT_EXPIRY,
actual_expiry: ENV.REDIS_ACTUAL_EXPIRY,
+1 -1
View File
@@ -165,7 +165,7 @@ vars! {
REDIS_BLOCKING_MAX_CONNECTIONS: u32 = 256u32;
// The encoding format used for Redis cache values.
REDIS_ENCODING_FORMAT: xredis::EncodingFormat = xredis::EncodingFormat::Json;
REDIS_ENCODING_FORMAT: xredis::EncodingFormat = xredis::EncodingFormat::Postcard;
// The level of LZ4 compression used for Redis cache values. A value of 0 disables compression (supports 1-12)
REDIS_COMPRESSION_LEVEL: i32 = 0i32;
// The compression algorithm used for Redis cache values. Currently only LZ4 is supported.
+31 -12
View File
@@ -4,6 +4,7 @@ use crate::models::analytics::{
};
use crate::routes::ApiError;
use crate::routes::analytics::MINECRAFT_SERVER_PLAYS;
use crate::util::error::Context;
use dashmap::{DashMap, DashSet};
use std::collections::HashMap;
use tracing::trace;
@@ -142,10 +143,15 @@ impl AnalyticsQueue {
)
})
.collect::<Vec<_>>();
let mut redis_connection = redis.connect().await?;
let mut redis_connection =
redis.connect().await.wrap_internal_err(
"connecting to redis for server play counts",
)?;
let results =
redis_connection.get_many_typed::<u32>(&redis_keys).await?;
let results = redis_connection
.get_many_typed::<u32>(&redis_keys)
.await
.wrap_internal_err("fetching server play counts from redis")?;
for (idx, count) in results.into_iter().enumerate() {
let new_count = if let Some(count) = count {
if count >= MINECRAFT_SERVER_PLAYS_LIMIT {
@@ -164,7 +170,8 @@ impl AnalyticsQueue {
new_count,
Some(MINECRAFT_SERVER_PLAYS_EXPIRY as i64),
)
.await?;
.await
.wrap_internal_err("writing server play count to redis")?;
}
let mut plays = client
@@ -198,10 +205,15 @@ impl AnalyticsQueue {
)
})
.collect::<Vec<_>>();
let mut redis_connection = redis.connect().await?;
let mut redis_connection = redis
.connect()
.await
.wrap_internal_err("connecting to redis for view counts")?;
let results =
redis_connection.get_many_typed::<u32>(&redis_keys).await?;
let results = redis_connection
.get_many_typed::<u32>(&redis_keys)
.await
.wrap_internal_err("fetching view counts from redis")?;
for (idx, count) in results.into_iter().enumerate() {
let new_count =
if let Some((views, monetized)) = raw_views.get_mut(idx) {
@@ -226,7 +238,8 @@ impl AnalyticsQueue {
let key = &redis_keys[idx];
redis_connection
.set(key, new_count, Some(6 * 60 * 60))
.await?;
.await
.wrap_internal_err("writing view count to redis")?;
}
let mut views = client.insert::<PageView>("views").await?;
@@ -267,10 +280,15 @@ impl AnalyticsQueue {
)
})
.collect::<Vec<_>>();
let mut redis_connection = redis.connect().await?;
let mut redis_connection = redis
.connect()
.await
.wrap_internal_err("connecting to redis for download counts")?;
let results =
redis_connection.get_many_typed::<u32>(&redis_keys).await?;
let results = redis_connection
.get_many_typed::<u32>(&redis_keys)
.await
.wrap_internal_err("fetching download counts from redis")?;
for (idx, count) in results.into_iter().enumerate() {
let new_count = if let Some(count) = count {
if count > 5 {
@@ -286,7 +304,8 @@ impl AnalyticsQueue {
let key = &redis_keys[idx];
redis_connection
.set(key, new_count, Some(6 * 60 * 60))
.await?;
.await
.wrap_internal_err("writing download count to redis")?;
}
let mut transaction = pool.begin().await?;
-6
View File
@@ -267,12 +267,6 @@ pub enum ApiError {
},
}
impl From<xredis::Error> for ApiError {
fn from(error: xredis::Error) -> Self {
Self::Database(error.into())
}
}
impl ApiError {
pub fn delphi(err: impl Into<eyre::Error>) -> Self {
Self::Delphi(err.into())