diff --git a/crates/nostr-sdk-db/examples/memory.rs b/crates/nostr-sdk-db/examples/memory.rs index 2cfc06e63..711de3ac3 100644 --- a/crates/nostr-sdk-db/examples/memory.rs +++ b/crates/nostr-sdk-db/examples/memory.rs @@ -10,7 +10,7 @@ use nostr_sdk_db::NostrDatabase; #[tokio::main] async fn main() { let keys = Keys::generate(); - let database = MemoryDatabase::new(); + let database = MemoryDatabase::new(true); for i in 0..50_000 { let event = EventBuilder::new_text_note(format!("Event #{i}"), &[]) diff --git a/crates/nostr-sdk-db/src/error.rs b/crates/nostr-sdk-db/src/error.rs index 4be1307c6..796684d27 100644 --- a/crates/nostr-sdk-db/src/error.rs +++ b/crates/nostr-sdk-db/src/error.rs @@ -14,6 +14,9 @@ pub enum DatabaseError { /// Not supported #[error("method not supported by current backend")] NotSupported, + /// Feature disabled + #[error("feature disabled for current backend")] + FeatureDisabled, /// Not found #[error("not found")] NotFound, diff --git a/crates/nostr-sdk-db/src/memory.rs b/crates/nostr-sdk-db/src/memory.rs index 734dd1983..b5f7d0ac3 100644 --- a/crates/nostr-sdk-db/src/memory.rs +++ b/crates/nostr-sdk-db/src/memory.rs @@ -24,17 +24,31 @@ impl From for DatabaseError { } /// Memory Database (RAM) -#[derive(Debug, Default)] +#[derive(Debug)] pub struct MemoryDatabase { + store_events: bool, seen_event_ids: Arc>>>, events: Arc>>, // TODO: add messages queue? (messages not sent) } +impl Default for MemoryDatabase { + fn default() -> Self { + Self::new(false) + } +} + impl MemoryDatabase { /// New Memory database - pub fn new() -> Self { - Self::default() + /// + /// If `store_events` arg is set to `true`, the seen events will be stored in memory (a lot of it could be used). + /// If it's set to `false`, only the [`EventId`] will be stored (instead of the full [`Event`]) + pub fn new(store_events: bool) -> Self { + Self { + store_events, + seen_event_ids: Arc::new(RwLock::new(HashMap::new())), + events: Arc::new(RwLock::new(HashMap::new())), + } } fn _event_id_seen( @@ -81,50 +95,54 @@ impl MemoryDatabase { ) -> Result { self.event_id_seen(event.id, None).await?; - if event.is_expired() || event.is_ephemeral() { - tracing::warn!("Event {} not saved: expired or ephemeral", event.id); - return Ok(false); - } + if self.store_events { + if event.is_expired() || event.is_ephemeral() { + tracing::warn!("Event {} not saved: expired or ephemeral", event.id); + return Ok(false); + } - let mut should_insert: bool = true; - - if event.is_replaceable() { - let filter: Filter = Filter::new() - .author(event.pubkey.to_string()) - .kind(event.kind); - let res: Vec = self._query(events, vec![filter]).await?; - if let Some(ev) = res.into_iter().next() { - if ev.created_at >= event.created_at { - should_insert = false; - } else if ev.created_at < event.created_at { - events.remove(&ev.id); + let mut should_insert: bool = true; + + if event.is_replaceable() { + let filter: Filter = Filter::new() + .author(event.pubkey.to_string()) + .kind(event.kind); + let res: Vec = self._query(events, vec![filter]).await?; + if let Some(ev) = res.into_iter().next() { + if ev.created_at >= event.created_at { + should_insert = false; + } else if ev.created_at < event.created_at { + events.remove(&ev.id); + } } - } - } else if event.is_parameterized_replaceable() { - match event.identifier() { - Some(identifier) => { - let filter: Filter = Filter::new() - .author(event.pubkey.to_string()) - .kind(event.kind) - .identifier(identifier); - let res: Vec = self._query(events, vec![filter]).await?; - if let Some(ev) = res.into_iter().next() { - if ev.created_at >= event.created_at { - should_insert = false; - } else if ev.created_at < event.created_at { - events.remove(&ev.id); + } else if event.is_parameterized_replaceable() { + match event.identifier() { + Some(identifier) => { + let filter: Filter = Filter::new() + .author(event.pubkey.to_string()) + .kind(event.kind) + .identifier(identifier); + let res: Vec = self._query(events, vec![filter]).await?; + if let Some(ev) = res.into_iter().next() { + if ev.created_at >= event.created_at { + should_insert = false; + } else if ev.created_at < event.created_at { + events.remove(&ev.id); + } } } + None => should_insert = false, } - None => should_insert = false, } - } - if should_insert { - events.insert(event.id, event); - Ok(true) + if should_insert { + events.insert(event.id, event); + Ok(true) + } else { + tracing::warn!("Event {} not saved: unknown", event.id); + Ok(false) + } } else { - tracing::warn!("Event {} not saved: unknown", event.id); Ok(false) } } @@ -180,27 +198,39 @@ impl NostrDatabase for MemoryDatabase { } async fn event_by_id(&self, event_id: EventId) -> Result { - let events = self.events.read().await; - events - .get(&event_id) - .cloned() - .ok_or(DatabaseError::NotFound) + if self.store_events { + let events = self.events.read().await; + events + .get(&event_id) + .cloned() + .ok_or(DatabaseError::NotFound) + } else { + Err(DatabaseError::FeatureDisabled) + } } async fn query(&self, filters: Vec) -> Result, Self::Err> { - let events = self.events.read().await; - self._query(&events, filters).await + if self.store_events { + let events = self.events.read().await; + self._query(&events, filters).await + } else { + Err(DatabaseError::FeatureDisabled) + } } async fn event_ids_by_filters(&self, filters: Vec) -> Result, Self::Err> { - let events = self.events.read().await; - let mut list: Vec = Vec::new(); - for event in events.values() { - if filters.match_event(event) { - list.push(event.id); + if self.store_events { + let events = self.events.read().await; + let mut list: Vec = Vec::new(); + for event in events.values() { + if filters.match_event(event) { + list.push(event.id); + } } + Ok(list) + } else { + Err(DatabaseError::FeatureDisabled) } - Ok(list) } async fn wipe(&self) -> Result<(), Self::Err> { diff --git a/crates/nostr-sdk/src/client/builder.rs b/crates/nostr-sdk/src/client/builder.rs index 1f5019983..a74a55c03 100644 --- a/crates/nostr-sdk/src/client/builder.rs +++ b/crates/nostr-sdk/src/client/builder.rs @@ -27,7 +27,7 @@ impl ClientBuilder { pub fn new(keys: &Keys) -> Self { Self { keys: keys.clone(), - database: Arc::new(MemoryDatabase::new()), + database: Arc::new(MemoryDatabase::default()), opts: Options::default(), #[cfg(feature = "nip46")] remote_signer: None, diff --git a/crates/nostr-sdk/src/relay/pool.rs b/crates/nostr-sdk/src/relay/pool.rs index 078af20e7..4b9c1d980 100644 --- a/crates/nostr-sdk/src/relay/pool.rs +++ b/crates/nostr-sdk/src/relay/pool.rs @@ -342,7 +342,7 @@ impl Drop for RelayPool { impl RelayPool { /// Create new `RelayPool` pub fn new(opts: RelayPoolOptions) -> Self { - Self::with_database(opts, Arc::new(MemoryDatabase::new())) + Self::with_database(opts, Arc::new(MemoryDatabase::default())) } /// New with database