From b316b4952d46ad964aa8397f9a3e57de8ba98b3b Mon Sep 17 00:00:00 2001 From: Benedikt Peetz Date: Mon, 24 Aug 2026 21:43:45 +0200 Subject: [PATCH 09/10] history/turtle: Do more work in the command handler, when loading history --- src/history/turtle.rs | 94 +++++++++++++++++++++++++++++-------------- 1 file changed, 63 insertions(+), 31 deletions(-) diff --git a/src/history/turtle.rs b/src/history/turtle.rs index 4ac931a19..15515fe15 100644 --- a/src/history/turtle.rs +++ b/src/history/turtle.rs @@ -5,7 +5,7 @@ atomic::{AtomicBool, Ordering}, mpsc, }, - thread::{self, JoinHandle, Thread}, + thread::JoinHandle, time::{Duration, SystemTime, UNIX_EPOCH}, }; @@ -19,30 +19,40 @@ pub(crate) struct HistoryDb; +#[derive(Debug, Clone)] +struct LoadedHistory { + map: HashMap, + + /// This field is effectively the same as `map.keys().collect()`. + /// + /// It just a cache. + keys: Vec, +} + #[derive(Debug)] struct HistoryDbInner { range: Option, session: uuid::Uuid, - loaded_history: Vec, + loaded_history: LoadedHistory, handler: Handler, } impl HistoryDbInner { - fn loaded_history(&mut self) -> &[History] { + fn loaded_history(&mut self) -> &LoadedHistory { if NEW_LOADED_HISTORY_AVAILABLE.load(Ordering::Relaxed) { if let Some(pre_loaded_history) = self.handler.load_history_resp() { self.loaded_history = pre_loaded_history; // PERFORMANCE: That seems to improve performance? <2026-08-24> - self.loaded_history.reverse(); + self.loaded_history.keys.reverse(); } flogf!( history, "Loaded history was requested, returning %d entries.", - self.loaded_history.len() + self.loaded_history.map.len() ); NEW_LOADED_HISTORY_AVAILABLE.store(false, Ordering::Relaxed); @@ -73,14 +83,7 @@ pub(super) fn create_empty() -> Self { /// Return the offsets of items in this file. pub(super) fn offsets(&self) -> Vec { - let out: Vec<_> = Self::with_inner_mut(|inner| { - inner - .loaded_history() - .iter() - .map(|h| h.id.clone()) - .collect() - }); - out + Self::with_inner_mut(|inner| inner.loaded_history().keys.clone()) } /// Return whether this file is empty. @@ -111,7 +114,10 @@ pub(super) fn load(history_path: &WString, _boundary_timestamp: SystemTime) -> S let inner = HistoryDbInner { session: uuid::Uuid::now_v7(), range, - loaded_history: vec![], + loaded_history: LoadedHistory { + map: HashMap::new(), + keys: vec![], + }, handler, }; @@ -128,20 +134,7 @@ pub(super) fn load(history_path: &WString, _boundary_timestamp: SystemTime) -> S /// Decode an item at a given offset. pub(super) fn decode_item(&self, id: HistoryId) -> Option { - Self::with_inner_mut(|inner| { - inner.loaded_history().iter().find(|h| h.id == id).map(|h| { - HistoryItem::new( - WString::from_str(&h.command), - super::Timestamps { - last_added: UNIX_EPOCH - + Duration::from_nanos_u128(h.timestamp.unix_timestamp_nanos() as u128), - first_added: UNIX_EPOCH - + Duration::from_nanos_u128(h.timestamp.unix_timestamp_nanos() as u128), - }, - super::PersistenceMode::Disk, - ) - }) - }) + Self::with_inner_mut(|inner| inner.loaded_history().map.get(&id).map(ToOwned::to_owned)) } } @@ -209,7 +202,7 @@ struct Handler { cmd_tx: Option>, - returned_loaded_history: Arc>>>, + returned_loaded_history: Arc>>, } #[derive(Debug)] @@ -311,8 +304,47 @@ fn start(daemon_socket: String) -> Self { base.expect("the client to still work") }; + let mut loaded_history_map = HashMap::new(); + + for item in loaded_history { + loaded_history_map.insert(item.id, { + HistoryItem::new( + WString::from_str(&item.command), + super::Timestamps { + last_added: UNIX_EPOCH + + Duration::from_nanos_u128( + item.timestamp.unix_timestamp_nanos() as u128, + ), + first_added: UNIX_EPOCH + + Duration::from_nanos_u128( + item.timestamp.unix_timestamp_nanos() as u128, + ), + }, + super::PersistenceMode::Disk, + ) + }); + } + + let keys = { + let mut base: Vec<_> = loaded_history_map + .iter() + .map(|(key, h)| (key, h.first_added_timestamp())) + .collect(); + + base.sort_by_key(|(_, time)| *time); + + // reverse this first here, so we can re-reverse it in the + // `loaded_history` function. + base.reverse(); + + base.into_iter().map(|(key, _)| key).copied().collect() + }; + let mut output = loaded_history_return.write().expect("not poisioned"); - (*output) = Some(loaded_history); + (*output) = Some(LoadedHistory { + keys, + map: loaded_history_map, + }); NEW_LOADED_HISTORY_AVAILABLE.store(true, Ordering::Relaxed); } } @@ -343,7 +375,7 @@ fn stop(&mut self) { flog!(history, "History db shutdown completed."); } - fn load_history_resp(&self) -> Option> { + fn load_history_resp(&self) -> Option { let mut output = None; let rx = &self.returned_loaded_history; -- 2.55.0