Skip to content

Commit 85c446b

Browse files
committed
Attach more data to CacheTimeout errrors
1 parent 4c3277b commit 85c446b

File tree

3 files changed

+44
-5
lines changed

3 files changed

+44
-5
lines changed

apps/labrinth/src/database/models/mod.rs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,13 @@ pub enum DatabaseError {
6767
SerdeCacheError(#[from] serde_json::Error),
6868
#[error("Schema error: {0}")]
6969
SchemaError(String),
70-
#[error("Timeout when waiting for cache subscriber")]
71-
CacheTimeout,
70+
#[error(
71+
"Timeout waiting on Redis cache lock ({locks_released}/{locks_waiting} released, spent {time_spent_pool_wait_ms}ms/{time_spent_total_ms}ms waiting on connections from pool)"
72+
)]
73+
CacheTimeout {
74+
locks_released: usize,
75+
locks_waiting: usize,
76+
time_spent_pool_wait_ms: u64,
77+
time_spent_total_ms: u64,
78+
},
7279
}

apps/labrinth/src/database/postgres_database.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,19 @@ pub async fn connect_all() -> Result<(PgPool, ReadOnlyPgPool), sqlx::Error> {
3535
info!("Initializing database connection");
3636
let database_url =
3737
dotenvy::var("DATABASE_URL").expect("`DATABASE_URL` not in .env");
38+
39+
let acquire_timeout = dotenvy::var("DATABASE_ACQUIRE_TIMEOUT_MS")
40+
.ok()
41+
.map(|x| {
42+
Duration::from_millis(
43+
x.parse::<u64>()
44+
.expect("DATABASE_ACQUIRE_TIMEOUT_MS must be a valid u64"),
45+
)
46+
})
47+
.unwrap_or_else(|| Duration::from_millis(30000)); // This is sqlx's default
48+
3849
let pool = PgPoolOptions::new()
50+
.acquire_timeout(acquire_timeout)
3951
.min_connections(
4052
dotenvy::var("DATABASE_MIN_CONNECTIONS")
4153
.ok()
@@ -54,6 +66,7 @@ pub async fn connect_all() -> Result<(PgPool, ReadOnlyPgPool), sqlx::Error> {
5466

5567
if let Ok(url) = dotenvy::var("READONLY_DATABASE_URL") {
5668
let ro_pool = PgPoolOptions::new()
69+
.acquire_timeout(acquire_timeout)
5770
.min_connections(
5871
dotenvy::var("READONLY_DATABASE_MIN_CONNECTIONS")
5972
.ok()

apps/labrinth/src/database/redis.rs

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ use std::fmt::{Debug, Display};
1616
use std::future::Future;
1717
use std::hash::Hash;
1818
use std::time::Duration;
19+
use std::time::Instant;
1920

2021
const DEFAULT_EXPIRY: i64 = 60 * 60 * 12; // 12 hours
2122
const ACTUAL_EXPIRY: i64 = 60 * 30; // 30 minutes
@@ -495,9 +496,13 @@ impl RedisPool {
495496
let mut interval =
496497
tokio::time::interval(Duration::from_millis(100));
497498
let start = Utc::now();
499+
let mut redis_budget = Duration::ZERO;
500+
498501
loop {
499502
let results = {
503+
let acquire_start = Instant::now();
500504
let mut connection = self.pool.get().await?;
505+
redis_budget += acquire_start.elapsed();
501506
cmd("MGET")
502507
.arg(
503508
subscribe_ids
@@ -516,12 +521,26 @@ impl RedisPool {
516521
.await?
517522
};
518523

519-
if results.into_iter().all(|x| x.is_none()) {
524+
let none_count =
525+
results.into_iter().filter(|x| x.is_some()).count();
526+
527+
// None of the locks exist anymore, we can continue
528+
if none_count == 0 {
520529
break;
521530
}
522531

523-
if (Utc::now() - start) > chrono::Duration::seconds(5) {
524-
return Err(DatabaseError::CacheTimeout);
532+
let spinning = Utc::now() - start;
533+
if spinning > chrono::Duration::seconds(5) {
534+
return Err(DatabaseError::CacheTimeout {
535+
locks_released: none_count,
536+
locks_waiting: subscribe_ids.len(),
537+
time_spent_pool_wait_ms: redis_budget.as_millis()
538+
as u64,
539+
time_spent_total_ms: spinning
540+
.num_milliseconds()
541+
.max(0)
542+
as u64,
543+
});
525544
}
526545

527546
interval.tick().await;

0 commit comments

Comments
 (0)