|
@@ -1,9 +1,24 @@
|
|
|
//! Rocks DB implementation of the storage layer
|
|
|
use crate::{event_filter::EventFilter, RocksDb};
|
|
|
+use futures::{Future, FutureExt, Stream};
|
|
|
use nostr_rs_storage_base::{Error, Storage};
|
|
|
use nostr_rs_types::types::Event;
|
|
|
use rocksdb::{BoundColumnFamily, DBIteratorWithThreadMode, DB};
|
|
|
-use std::{collections::VecDeque, sync::Arc};
|
|
|
+use std::{
|
|
|
+ collections::VecDeque,
|
|
|
+ pin::Pin,
|
|
|
+ sync::Arc,
|
|
|
+ task::{Context, Poll},
|
|
|
+};
|
|
|
+
|
|
|
+type CurrentEventByPrefixFuture<'a> = Pin<
|
|
|
+ Box<
|
|
|
+ dyn Future<
|
|
|
+ Output = Result<Option<nostr_rs_types::types::Event>, nostr_rs_storage_base::Error>,
|
|
|
+ > + Send
|
|
|
+ + 'a,
|
|
|
+ >,
|
|
|
+>;
|
|
|
|
|
|
pub struct WrapperIterator<'a> {
|
|
|
/// Reference to the rocks db database. This is useful to load the event
|
|
@@ -29,6 +44,8 @@ pub struct WrapperIterator<'a> {
|
|
|
pub prefixes: VecDeque<Vec<u8>>,
|
|
|
pub limit: Option<usize>,
|
|
|
pub returned: usize,
|
|
|
+
|
|
|
+ pub current_event_by_prefix: Option<CurrentEventByPrefixFuture<'a>>,
|
|
|
}
|
|
|
|
|
|
impl<'a> WrapperIterator<'a> {
|
|
@@ -47,64 +64,79 @@ impl<'a> WrapperIterator<'a> {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-impl<'a> Iterator for WrapperIterator<'a> {
|
|
|
+impl<'a> Stream for WrapperIterator<'a> {
|
|
|
type Item = Result<Event, Error>;
|
|
|
|
|
|
- fn next(&mut self) -> Option<Self::Item> {
|
|
|
+ fn size_hint(&self) -> (usize, Option<usize>) {
|
|
|
+ (0, None)
|
|
|
+ }
|
|
|
+
|
|
|
+ fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
|
|
|
if Some(self.returned) == self.limit {
|
|
|
- return None;
|
|
|
+ return Poll::Ready(None);
|
|
|
}
|
|
|
- if self.secondary_index_iterator.is_none() {
|
|
|
- if self.namespace.is_some() {
|
|
|
- self.select_next_prefix_using_secondary_index()?;
|
|
|
- } else {
|
|
|
- // No secondary index is used to query, this means the query is
|
|
|
- // using the ID filter, so it is more efficient to use the
|
|
|
- // primary index to prefetch events that may satisfy the query
|
|
|
- loop {
|
|
|
- let prefix = self.prefixes.pop_front()?;
|
|
|
- if let Ok(Some(event)) = self.db.get_event(prefix).await {
|
|
|
- if let Some(filter) = &self.filter {
|
|
|
- if filter.check_event(&event) {
|
|
|
- self.returned += 1;
|
|
|
- return Some(Ok(event));
|
|
|
- }
|
|
|
- } else {
|
|
|
- self.returned += 1;
|
|
|
- return Some(Ok(event));
|
|
|
+
|
|
|
+ let this = Pin::into_inner(self);
|
|
|
+ let db = this.db;
|
|
|
+
|
|
|
+ if let Some(mut current_event_filter) = this.current_event_by_prefix.take() {
|
|
|
+ match current_event_filter.poll_unpin(cx) {
|
|
|
+ Poll::Ready(Ok(Some(event))) => {
|
|
|
+ // event is ready, apply the neccesary filters
|
|
|
+ if let Some(filter) = &this.filter {
|
|
|
+ if filter.check_event(&event) {
|
|
|
+ this.returned += 1;
|
|
|
+ return Poll::Ready(Some(Ok(event)));
|
|
|
}
|
|
|
+ } else {
|
|
|
+ this.returned += 1;
|
|
|
+ return Poll::Ready(Some(Ok(event)));
|
|
|
}
|
|
|
}
|
|
|
+ Poll::Ready(Err(x)) => return Poll::Ready(Some(Err(x))),
|
|
|
+ Poll::Pending => {
|
|
|
+ // add it back
|
|
|
+ this.current_event_by_prefix = Some(current_event_filter);
|
|
|
+ return Poll::Pending;
|
|
|
+ }
|
|
|
+ _ => {}
|
|
|
}
|
|
|
}
|
|
|
+ let secondary_index = if let Some(iterator) = this.secondary_index_iterator.as_mut() {
|
|
|
+ iterator
|
|
|
+ } else {
|
|
|
+ return Poll::Ready(None);
|
|
|
+ };
|
|
|
|
|
|
- loop {
|
|
|
- loop {
|
|
|
- let secondary_index = self.secondary_index_iterator.as_mut()?;
|
|
|
- let (key, value) = match secondary_index.next() {
|
|
|
- Some(Ok((k, v))) => (k, v),
|
|
|
- _ => {
|
|
|
- // break this loop to select next available prefix
|
|
|
- break;
|
|
|
+ match secondary_index.next() {
|
|
|
+ Some(Ok((key, value))) => {
|
|
|
+ if !key.starts_with(&this.current_prefix) {
|
|
|
+ if this.select_next_prefix_using_secondary_index().is_none() {
|
|
|
+ return Poll::Ready(None);
|
|
|
}
|
|
|
- };
|
|
|
- if !key.starts_with(&self.current_prefix) {
|
|
|
- break;
|
|
|
+ } else {
|
|
|
+ // query the database to get the record
|
|
|
+ this.current_event_by_prefix = Some(db.get_event(value));
|
|
|
}
|
|
|
- if let Ok(Some(event)) = self.db.get_event(value) {
|
|
|
- if let Some(filter) = &self.filter {
|
|
|
- if filter.check_event(&event) {
|
|
|
- self.returned += 1;
|
|
|
- return Some(Ok(event));
|
|
|
- }
|
|
|
- } else {
|
|
|
- self.returned += 1;
|
|
|
- return Some(Ok(event));
|
|
|
+
|
|
|
+ Poll::Pending
|
|
|
+ }
|
|
|
+ Some(Err(err)) => Poll::Ready(Some(Err(Error::Internal(err.to_string())))),
|
|
|
+ None => {
|
|
|
+ if this.namespace.is_some() {
|
|
|
+ if this.select_next_prefix_using_secondary_index().is_none() {
|
|
|
+ return Poll::Ready(None);
|
|
|
}
|
|
|
+ } else {
|
|
|
+ // No secondary index is used to query, this means the query is
|
|
|
+ // using the ID filter, so it is more efficient to use the
|
|
|
+ // primary index to prefetch events that may satisfy the query
|
|
|
+ let current_event_by_prefix =
|
|
|
+ this.prefixes.pop_front().map(|prefix| db.get_event(prefix));
|
|
|
+ this.current_event_by_prefix = current_event_by_prefix;
|
|
|
}
|
|
|
+ Poll::Pending
|
|
|
}
|
|
|
- // Select next prefix if available, or exists
|
|
|
- self.select_next_prefix_using_secondary_index()?;
|
|
|
}
|
|
|
}
|
|
|
}
|