From c726d5d9408519b435c1bfdab7bbf518f4270e83 Mon Sep 17 00:00:00 2001 From: aecsocket Date: Wed, 25 Mar 2026 22:23:16 +0000 Subject: [PATCH 1/3] wip: online status fix --- ...5aabf13190a0b336089e6521022069813cf17.json | 5 +- ...dada47fb382a76fdcabad2077fb1ef6d1010a.json | 5 +- ...ba49f4963315fd7667c6db96e6153e54a2fd2.json | 3 +- ...de1e7cddd68ac956143bef994104280a8dc07.json | 3 +- apps/labrinth/src/env.rs | 1 + apps/labrinth/src/queue/server_ping.rs | 92 +++++++++++++++---- 6 files changed, 85 insertions(+), 24 deletions(-) diff --git a/apps/labrinth/.sqlx/query-10e2a3b31ba94b93ed2d6c9753a5aabf13190a0b336089e6521022069813cf17.json b/apps/labrinth/.sqlx/query-10e2a3b31ba94b93ed2d6c9753a5aabf13190a0b336089e6521022069813cf17.json index 53cf350301..ed3215a695 100644 --- a/apps/labrinth/.sqlx/query-10e2a3b31ba94b93ed2d6c9753a5aabf13190a0b336089e6521022069813cf17.json +++ b/apps/labrinth/.sqlx/query-10e2a3b31ba94b93ed2d6c9753a5aabf13190a0b336089e6521022069813cf17.json @@ -29,7 +29,8 @@ "low", "medium", "high", - "severe" + "severe", + "malware" ] } } @@ -45,7 +46,7 @@ false, true, false, - false + true ] }, "hash": "10e2a3b31ba94b93ed2d6c9753a5aabf13190a0b336089e6521022069813cf17" diff --git a/apps/labrinth/.sqlx/query-263ad3654f544ffb6061c839d49dada47fb382a76fdcabad2077fb1ef6d1010a.json b/apps/labrinth/.sqlx/query-263ad3654f544ffb6061c839d49dada47fb382a76fdcabad2077fb1ef6d1010a.json index 23c13cd169..a854453595 100644 --- a/apps/labrinth/.sqlx/query-263ad3654f544ffb6061c839d49dada47fb382a76fdcabad2077fb1ef6d1010a.json +++ b/apps/labrinth/.sqlx/query-263ad3654f544ffb6061c839d49dada47fb382a76fdcabad2077fb1ef6d1010a.json @@ -44,7 +44,8 @@ "low", "medium", "high", - "severe" + "severe", + "malware" ] } } @@ -79,7 +80,7 @@ true, false, false, - false, + true, null ] }, diff --git a/apps/labrinth/.sqlx/query-9369f0659c5fbd08463923a9b2bba49f4963315fd7667c6db96e6153e54a2fd2.json b/apps/labrinth/.sqlx/query-9369f0659c5fbd08463923a9b2bba49f4963315fd7667c6db96e6153e54a2fd2.json index 8ca4b69491..419c0ff25c 100644 --- a/apps/labrinth/.sqlx/query-9369f0659c5fbd08463923a9b2bba49f4963315fd7667c6db96e6153e54a2fd2.json +++ b/apps/labrinth/.sqlx/query-9369f0659c5fbd08463923a9b2bba49f4963315fd7667c6db96e6153e54a2fd2.json @@ -25,7 +25,8 @@ "low", "medium", "high", - "severe" + "severe", + "malware" ] } } diff --git a/apps/labrinth/.sqlx/query-f2054ae7dcc89b21ed6b2f04526de1e7cddd68ac956143bef994104280a8dc07.json b/apps/labrinth/.sqlx/query-f2054ae7dcc89b21ed6b2f04526de1e7cddd68ac956143bef994104280a8dc07.json index 8cbe94abd5..c20c8ecdab 100644 --- a/apps/labrinth/.sqlx/query-f2054ae7dcc89b21ed6b2f04526de1e7cddd68ac956143bef994104280a8dc07.json +++ b/apps/labrinth/.sqlx/query-f2054ae7dcc89b21ed6b2f04526de1e7cddd68ac956143bef994104280a8dc07.json @@ -22,7 +22,8 @@ "low", "medium", "high", - "severe" + "severe", + "malware" ] } } diff --git a/apps/labrinth/src/env.rs b/apps/labrinth/src/env.rs index d275e2caee..4d6643417d 100644 --- a/apps/labrinth/src/env.rs +++ b/apps/labrinth/src/env.rs @@ -293,4 +293,5 @@ vars! { SERVER_PING_RETRIES: usize = 3usize; SERVER_PING_MIN_INTERVAL_SEC: u64 = 30u64 * 60; SERVER_PING_TIMEOUT_MS: u64 = 3u64 * 1000; + SERVER_PING_MAX_FAIL_COUNT: u32 = 3u32; } diff --git a/apps/labrinth/src/queue/server_ping.rs b/apps/labrinth/src/queue/server_ping.rs index c65ceb80ba..3bef14457a 100644 --- a/apps/labrinth/src/queue/server_ping.rs +++ b/apps/labrinth/src/queue/server_ping.rs @@ -24,6 +24,7 @@ pub struct ServerPingQueue { } pub const REDIS_NAMESPACE: &str = "minecraft_java_server_ping"; +pub const REDIS_FAILURE_NAMESPACE: &str = "minecraft_java_server_ping_failures"; pub const CLICKHOUSE_TABLE: &str = "minecraft_java_server_pings"; impl ServerPingQueue { @@ -118,27 +119,82 @@ impl ServerPingQueue { .await .wrap_err("failed to write ping record")?; - redis - .set_serialized_to_json( - REDIS_NAMESPACE, - project_id, - ping, + if data.is_some() { + // ping succeeded; immediately update its online status in redis + + redis + .set_serialized_to_json( + REDIS_NAMESPACE, + project_id, + ping, + None, + ) + .await + .wrap_err("failed to set redis key")?; + + redis + .delete(REDIS_FAILURE_NAMESPACE, project_id) + .await + .wrap_err("failed to delete failure count")?; + } else { + // ping failed; if it's failed too many times, mark it as offline in redis + // otherwise, just add to the fail counter + + let mut failure_count: u32 = redis + .get(REDIS_FAILURE_NAMESPACE, &project_id.to_string()) + .await + .inspect_err(|err| { + warn!("failed to get failure count: {err:#}") + }) + .ok() + .flatten() + .and_then(|s| s.parse().ok()) + .unwrap_or(0); + + failure_count = failure_count.saturating_add(1); + + redis + .set( + REDIS_FAILURE_NAMESPACE, + &project_id.to_string(), + &failure_count.to_string(), + None, + ) + .await + .inspect_err(|err| { + warn!("failed to set failure count: {err:#}") + }) + .ok(); + + if failure_count >= ENV.SERVER_PING_MAX_FAIL_COUNT { + redis + .set_serialized_to_json( + REDIS_NAMESPACE, + project_id, + ping, + None, + ) + .await + .inspect_err(|err| { + warn!("failed to set failed ping record in redis: {err:#}") + }) + .ok(); + } + } + + if data.is_some() { + DBProject::clear_cache( + (*project_id).into(), + None, None, + &self.redis, ) .await - .wrap_err("failed to set redis key")?; - - DBProject::clear_cache( - (*project_id).into(), - None, - None, - &self.redis, - ) - .await - .inspect_err(|err| { - warn!("failed to clear project cache: {err:#}") - }) - .ok(); + .inspect_err(|err| { + warn!("failed to clear project cache: {err:#}") + }) + .ok(); + } } ch.end() From 59f8a178fad3971291ed64626c8084db4d3fae8c Mon Sep 17 00:00:00 2001 From: aecsocket Date: Wed, 25 Mar 2026 22:40:04 +0000 Subject: [PATCH 2/3] use INCR --- apps/labrinth/CLAUDE.md | 2 +- apps/labrinth/src/database/redis/mod.rs | 14 +++++++++ apps/labrinth/src/env.rs | 2 +- apps/labrinth/src/queue/server_ping.rs | 38 ++++++------------------- 4 files changed, 25 insertions(+), 31 deletions(-) diff --git a/apps/labrinth/CLAUDE.md b/apps/labrinth/CLAUDE.md index 2d496b028d..fe4b705308 100644 --- a/apps/labrinth/CLAUDE.md +++ b/apps/labrinth/CLAUDE.md @@ -8,7 +8,7 @@ When the user refers to "perform[ing] pre-PR checks", do the following: - Run `cargo clippy -p labrinth --all-targets` — there must be ZERO warnings, otherwise CI will fail - DO NOT run tests unless explicitly requested (they take a long time) -- Prepare the sqlx cache: cd into `apps/labrinth` and run `cargo sqlx prepare` +- Prepare the sqlx cache: cd into `apps/labrinth` and run `cargo sqlx prepare -- --tests` - NEVER run `cargo sqlx prepare --workspace` ## Testing diff --git a/apps/labrinth/src/database/redis/mod.rs b/apps/labrinth/src/database/redis/mod.rs index 1b8ac30c01..51e94cfb16 100644 --- a/apps/labrinth/src/database/redis/mod.rs +++ b/apps/labrinth/src/database/redis/mod.rs @@ -788,6 +788,20 @@ impl RedisConnection { .await?; Ok(values) } + + #[tracing::instrument(skip(self))] + pub async fn incr( + &mut self, + namespace: &str, + id: &str, + ) -> Result, DatabaseError> { + let key = format!("{}_{namespace}:{id}", self.meta_namespace); + let value = cmd("INCR") + .arg(key) + .query_async(&mut self.connection) + .await?; + Ok(value) + } } #[derive(Serialize, Deserialize)] diff --git a/apps/labrinth/src/env.rs b/apps/labrinth/src/env.rs index 4d6643417d..ab5c6165b8 100644 --- a/apps/labrinth/src/env.rs +++ b/apps/labrinth/src/env.rs @@ -293,5 +293,5 @@ vars! { SERVER_PING_RETRIES: usize = 3usize; SERVER_PING_MIN_INTERVAL_SEC: u64 = 30u64 * 60; SERVER_PING_TIMEOUT_MS: u64 = 3u64 * 1000; - SERVER_PING_MAX_FAIL_COUNT: u32 = 3u32; + SERVER_PING_MAX_FAIL_COUNT: u64 = 3u64; } diff --git a/apps/labrinth/src/queue/server_ping.rs b/apps/labrinth/src/queue/server_ping.rs index 3bef14457a..8f83935569 100644 --- a/apps/labrinth/src/queue/server_ping.rs +++ b/apps/labrinth/src/queue/server_ping.rs @@ -140,33 +140,14 @@ impl ServerPingQueue { // ping failed; if it's failed too many times, mark it as offline in redis // otherwise, just add to the fail counter - let mut failure_count: u32 = redis - .get(REDIS_FAILURE_NAMESPACE, &project_id.to_string()) + let failure_count = redis + .incr(REDIS_FAILURE_NAMESPACE, &project_id.to_string()) .await - .inspect_err(|err| { - warn!("failed to get failure count: {err:#}") - }) - .ok() - .flatten() - .and_then(|s| s.parse().ok()) - .unwrap_or(0); + .wrap_err("failed to increment failure count")?; - failure_count = failure_count.saturating_add(1); - - redis - .set( - REDIS_FAILURE_NAMESPACE, - &project_id.to_string(), - &failure_count.to_string(), - None, - ) - .await - .inspect_err(|err| { - warn!("failed to set failure count: {err:#}") - }) - .ok(); - - if failure_count >= ENV.SERVER_PING_MAX_FAIL_COUNT { + if let Some(count) = failure_count + && count >= ENV.SERVER_PING_MAX_FAIL_COUNT + { redis .set_serialized_to_json( REDIS_NAMESPACE, @@ -175,10 +156,9 @@ impl ServerPingQueue { None, ) .await - .inspect_err(|err| { - warn!("failed to set failed ping record in redis: {err:#}") - }) - .ok(); + .wrap_err( + "failed to set failed ping record in redis", + )?; } } From da30c155a065a954ecdf0fe41767a007c82aeb2c Mon Sep 17 00:00:00 2001 From: aecsocket Date: Wed, 25 Mar 2026 22:43:43 +0000 Subject: [PATCH 3/3] properly clear cache --- apps/labrinth/src/queue/server_ping.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/apps/labrinth/src/queue/server_ping.rs b/apps/labrinth/src/queue/server_ping.rs index 8f83935569..b53708290e 100644 --- a/apps/labrinth/src/queue/server_ping.rs +++ b/apps/labrinth/src/queue/server_ping.rs @@ -119,6 +119,7 @@ impl ServerPingQueue { .await .wrap_err("failed to write ping record")?; + let mut updated_project = false; if data.is_some() { // ping succeeded; immediately update its online status in redis @@ -131,6 +132,7 @@ impl ServerPingQueue { ) .await .wrap_err("failed to set redis key")?; + updated_project = true; redis .delete(REDIS_FAILURE_NAMESPACE, project_id) @@ -159,10 +161,11 @@ impl ServerPingQueue { .wrap_err( "failed to set failed ping record in redis", )?; + updated_project = true; } } - if data.is_some() { + if updated_project { DBProject::clear_cache( (*project_id).into(), None,