From d9bf85884bfd1df6e47d5b4a0c0be8c0fc7b8069 Mon Sep 17 00:00:00 2001 From: yggverse Date: Sat, 16 Nov 2024 17:04:43 +0200 Subject: [PATCH] add memory cache for auth index --- src/profile/identity.rs | 12 ++--- src/profile/identity/gemini/auth.rs | 30 ++++++++++- src/profile/identity/gemini/auth/database.rs | 8 ++- src/profile/identity/gemini/auth/memory.rs | 52 +++++++++++++++++++ .../identity/gemini/auth/memory/error.rs | 4 ++ 5 files changed, 96 insertions(+), 10 deletions(-) create mode 100644 src/profile/identity/gemini/auth/memory.rs create mode 100644 src/profile/identity/gemini/auth/memory/error.rs diff --git a/src/profile/identity.rs b/src/profile/identity.rs index 0430d34f..d575bce8 100644 --- a/src/profile/identity.rs +++ b/src/profile/identity.rs @@ -41,13 +41,11 @@ impl Identity { /// /// https://geminiprotocol.net/docs/protocol-specification.gmi#client-certificates pub fn gemini(&self, request: &str) -> Option { - if let Ok(auth_records) = self.gemini.auth.database.records(Some(request)) { - for auth_record in auth_records { - if let Ok(gemini_records) = self.gemini.database.records() { - for gemini_record in gemini_records { - if gemini_record.id == auth_record.profile_identity_gemini_id { - return Some(gemini_record.pem); - } + for profile_identity_gemini_id in self.gemini.auth.memory.starts_with(request) { + if let Ok(gemini_records) = self.gemini.database.records() { + for gemini_record in gemini_records { + if gemini_record.id == profile_identity_gemini_id { + return Some(gemini_record.pem); } } } diff --git a/src/profile/identity/gemini/auth.rs b/src/profile/identity/gemini/auth.rs index 6f4342b3..403b10fe 100644 --- a/src/profile/identity/gemini/auth.rs +++ b/src/profile/identity/gemini/auth.rs @@ -1,13 +1,16 @@ mod database; +mod memory; use database::Database; +use memory::Memory; use sqlite::{Connection, Transaction}; use std::{rc::Rc, sync::RwLock}; /// API for `profile_identity_gemini_id` + `url` auth pairs operations pub struct Auth { - pub database: Rc, + // pub database: Rc, + pub memory: Rc, } impl Auth { @@ -15,8 +18,31 @@ impl Auth { /// Create new `Self` pub fn new(connection: Rc>) -> Self { + // Init children components + let database = Rc::new(Database::new(connection)); + let memory = Rc::new(Memory::new()); + + // Build initial index + match database.records(None) { + Ok(records) => { + for record in records { + if record.is_active { + if memory + .add(record.url, record.profile_identity_gemini_id) + .is_err() + { + todo!() + } + } + } + } + Err(reason) => todo!("{reason}"), + } + + // Return new `Self` Self { - database: Rc::new(Database::new(connection)), + // database, + memory, } } } diff --git a/src/profile/identity/gemini/auth/database.rs b/src/profile/identity/gemini/auth/database.rs index 196aba61..32ebcf7f 100644 --- a/src/profile/identity/gemini/auth/database.rs +++ b/src/profile/identity/gemini/auth/database.rs @@ -5,6 +5,8 @@ use sqlite::{Connection, Error, Transaction}; pub struct Table { //pub id: i64, pub profile_identity_gemini_id: i64, + pub is_active: bool, + pub url: String, } /// Storage for `profile_identity_gemini_id` + `url` auth pairs @@ -56,7 +58,9 @@ pub fn init(tx: &Transaction) -> Result { pub fn select(tx: &Transaction, url: Option<&str>) -> Result, Error> { let mut stmt = tx.prepare( "SELECT `id`, - `profile_identity_gemini_id` + `profile_identity_gemini_id`, + `is_active`, + `url` FROM `profile_identity_gemini_auth` WHERE `url` LIKE ?", @@ -66,6 +70,8 @@ pub fn select(tx: &Transaction, url: Option<&str>) -> Result, Error> Ok(Table { //id: row.get(0)?, profile_identity_gemini_id: row.get(1)?, + is_active: row.get(2)?, + url: row.get(3)?, }) })?; diff --git a/src/profile/identity/gemini/auth/memory.rs b/src/profile/identity/gemini/auth/memory.rs new file mode 100644 index 00000000..d4d09396 --- /dev/null +++ b/src/profile/identity/gemini/auth/memory.rs @@ -0,0 +1,52 @@ +mod error; +use error::Error; + +use std::{cell::RefCell, collections::HashMap}; + +/// Reduce disk usage by cache Auth index in memory +pub struct Memory { + index: RefCell>, +} + +impl Memory { + // Constructors + + /// Create new `Self` + pub fn new() -> Self { + Self { + index: RefCell::new(HashMap::new()), + } + } + + // Actions + + /// Add new record with `url` as key and `profile_identity_gemini_id` as value + /// * validate record with same key does not exist yet + pub fn add(&self, url: String, profile_identity_gemini_id: i64) -> Result<(), Error> { + match self + .index + .borrow_mut() + .insert(url, profile_identity_gemini_id) + { + Some(_) => Err(Error::Overwrite), // @TODO prevent? + None => Ok(()), + } + } + + /* @TODO update feature + /// Cleanup index + pub fn clear(&self, url: &str) { + self.index.borrow_mut().clear() + } */ + + /// Search for `profile_identity_gemini_id` by `url` starts with given substring + pub fn starts_with(&self, prefix: &str) -> Vec { + let mut result = Vec::new(); + for (url, &profile_identity_gemini_id) in self.index.borrow().iter() { + if url.starts_with(prefix) { + result.push(profile_identity_gemini_id) + } + } + result + } +} diff --git a/src/profile/identity/gemini/auth/memory/error.rs b/src/profile/identity/gemini/auth/memory/error.rs new file mode 100644 index 00000000..4d8262af --- /dev/null +++ b/src/profile/identity/gemini/auth/memory/error.rs @@ -0,0 +1,4 @@ +#[derive(Debug)] +pub enum Error { + Overwrite, +}