feat(labrinth): Redis Cluster (#6771)

* chore(labrinth): bump to redis 1.4.1

* feat(labrinth): redis cluster

* chore: cleanup

* feat(labrinth): cache locking

* fix(labrinth): clippy

* chore(labrinth): cleanup env, remove postcard support

* chore(ci): fix test env for labrinth

* chore(labrinth): bump all key versions

* chore(labrinth): improve redis key identities handling

* chore(labrinth): simplify deadline handling

* chore(labrinth): remove unused lease tracking

* chore(labrinth): remove distributed cache locking for now

* chore(labrinth): improve redis backend init error

* feat(labrinth): expose redis read replica strategy

* chore(ci): remove other connection mode tests

* chore: split xredis crate

* feat(xredis): primaries routing

* chore: tombi fmt

* chore: clippy

* chore: update query cache
This commit is contained in:
François-Xavier Talbot
2026-07-23 11:35:02 +02:00
committed by GitHub
parent 11af2651ef
commit b4d681e713
146 changed files with 4741 additions and 2000 deletions
+20 -14
View File
@@ -1,4 +1,3 @@
use crate::database::redis::RedisPool;
use crate::env::ENV;
use crate::models::ids::PayoutId;
use crate::routes::ApiError;
@@ -9,12 +8,23 @@ use chrono::{DateTime, Datelike, Utc};
use serde::{Deserialize, Serialize};
use std::str::FromStr;
use std::time::Duration;
use xredis::RedisPool;
pub const MODRINTH_GENERATED_PDF_TYPE: HeaderName =
HeaderName::from_static("modrinth-generated-pdf-type");
pub const MODRINTH_PAYMENT_ID: HeaderName =
HeaderName::from_static("modrinth-payment-id");
pub const PAYMENT_STATEMENTS_NAMESPACE: &str = "payment_statements:v1";
pub const PAYMENT_STATEMENTS_NAMESPACE: &str = "payment_statements:v3";
const REDIS_TIMEOUT_MARGIN_MS: u64 = 250;
pub(crate) fn payment_statement_key(
redis: &RedisPool,
payment_id: &PayoutId,
) -> String {
redis
.key()
.with_slot(PAYMENT_STATEMENTS_NAMESPACE, payment_id, payment_id)
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct PaymentStatement {
@@ -180,23 +190,19 @@ impl GotenbergClient {
&self,
statement: &PaymentStatement,
) -> Result<GotenbergDocument, ApiError> {
let mut redis = self
.redis
.connect()
.await
.wrap_internal_err("failed to get Redis connection")?;
self.generate_payment_statement(statement).await?;
let timeout_ms = ENV.GOTENBERG_TIMEOUT;
let redis_timeout_ms =
timeout_ms.saturating_sub(REDIS_TIMEOUT_MARGIN_MS).max(1);
let client_timeout_ms = timeout_ms.max(redis_timeout_ms + 1);
let response_key =
payment_statement_key(&self.redis, &statement.payment_id);
let [_key, document] = tokio::time::timeout(
Duration::from_millis(timeout_ms),
redis.brpop(
PAYMENT_STATEMENTS_NAMESPACE,
&statement.payment_id.to_string(),
None,
),
Duration::from_millis(client_timeout_ms),
self.redis
.brpop(&response_key, Duration::from_millis(redis_timeout_ms)),
)
.await
.wrap_internal_err("Gotenberg document generation timed out")?