From bcadecdc3b844e83253167c34e1689f6ad87919c Mon Sep 17 00:00:00 2001 From: timedout Date: Wed, 17 Jun 2026 17:59:28 +0100 Subject: [PATCH] feat: Add `!admin debug rooms-by-extremity-count` command --- src/admin/debug/commands.rs | 52 +++++++++++++++++++++++++++++++++- src/admin/debug/mod.rs | 5 ++++ src/service/rooms/state/mod.rs | 10 +++++++ 3 files changed, 66 insertions(+), 1 deletion(-) diff --git a/src/admin/debug/commands.rs b/src/admin/debug/commands.rs index f9a678f19..87a81d893 100644 --- a/src/admin/debug/commands.rs +++ b/src/admin/debug/commands.rs @@ -31,7 +31,7 @@ use service::rooms::{ }; use tracing_subscriber::EnvFilter; -use crate::admin_command; +use crate::{PAGE_SIZE, admin_command}; #[admin_command] pub(super) async fn echo(&self, message: Vec) -> Result { @@ -1138,3 +1138,53 @@ pub(super) async fn send_test_email(&self) -> Result { Ok(()) } + +#[admin_command] +pub(super) async fn rooms_by_extremity_count(&self, page: Option) -> Result { + let page = page.unwrap_or(1); + // My Giant Chain:tm: + let mapped: HashMap = self + .services + .rooms + .state + .all_forward_extremities() + .ready_fold(HashMap::new(), move |mut map, (room_id, _)| { + let count: u64 = map.get(&room_id).copied().unwrap_or(0); + map.insert(room_id, count.saturating_add(1)); + map + }) + .await + .into_iter() + .filter_map(|(room_id, count)| (count >= 2).then_some((room_id, count))) + .collect(); + if mapped.is_empty() { + return Err!("No more rooms."); + } + + let mut rooms = mapped.keys().collect::>(); + rooms.sort_by_key(|room_id| { + mapped + .get(*room_id) + .copied() + .expect("keys must have values") + }); + rooms.reverse(); + + let body = rooms + .into_iter() + .stream() + .skip(page.saturating_sub(1).saturating_mul(PAGE_SIZE)) + .take(PAGE_SIZE) + .map(|room_id| { + format!("{room_id}: {}", mapped.get(room_id).copied().expect("keys must have values")) + }) + .collect::>() + .await; + + self.write_str(&format!( + "Rooms by extremity count ({}):\n```\n{}\n```", + body.len(), + body.join("\n") + )) + .await +} diff --git a/src/admin/debug/mod.rs b/src/admin/debug/mod.rs index a9478ce82..0a2e0ae7e 100644 --- a/src/admin/debug/mod.rs +++ b/src/admin/debug/mod.rs @@ -237,6 +237,11 @@ pub enum DebugCommand { /// Send a test email to the invoking admin's email address SendTestEmail, + /// Lists room IDs by forward extremity count in descending order + RoomsByExtremityCount { + page: Option, + }, + /// Developer test stubs #[command(subcommand)] #[allow(non_snake_case)] diff --git a/src/service/rooms/state/mod.rs b/src/service/rooms/state/mod.rs index ccc4be87e..bb0061f08 100644 --- a/src/service/rooms/state/mod.rs +++ b/src/service/rooms/state/mod.rs @@ -371,6 +371,16 @@ impl Service { .deserialized() } + pub fn all_forward_extremities( + &self, + ) -> impl Stream + Send { + self.db + .roomid_pduleaves + .keys() + .map_ok(|(room_id, event_id): (OwnedRoomId, OwnedEventId)| (room_id, event_id)) + .ignore_err() + } + pub fn get_forward_extremities<'a>( &'a self, room_id: &'a RoomId,