|
@@ -3,9 +3,13 @@ use crate::{
|
|
|
transaction::Type, AccountId, Amount, Error, Filter, PaymentFrom, PaymentId, RevId, Status,
|
|
|
Tag, Transaction, TxId,
|
|
|
};
|
|
|
-use std::{cmp::Ordering, collections::HashMap, sync::Arc};
|
|
|
+use std::{
|
|
|
+ cmp::Ordering,
|
|
|
+ collections::HashMap,
|
|
|
+ sync::{atomic::AtomicUsize, Arc},
|
|
|
+};
|
|
|
use tokio::sync::{
|
|
|
- mpsc::{self, Receiver, Sender},
|
|
|
+ mpsc::{self, error::TrySendError, Receiver, Sender},
|
|
|
RwLock,
|
|
|
};
|
|
|
|
|
@@ -16,7 +20,9 @@ where
|
|
|
S: Storage + Sync + Send,
|
|
|
{
|
|
|
config: Config<S>,
|
|
|
- listeners: RwLock<HashMap<Filter, Vec<Sender<Transaction>>>>,
|
|
|
+ senders: RwLock<HashMap<usize, Sender<Transaction>>>,
|
|
|
+ subscriptions: RwLock<HashMap<Filter, Vec<usize>>>,
|
|
|
+ sender_index: AtomicUsize,
|
|
|
}
|
|
|
|
|
|
impl<S> Ledger<S>
|
|
@@ -27,25 +33,38 @@ where
|
|
|
pub fn new(config: Config<S>) -> Arc<Self> {
|
|
|
Arc::new(Self {
|
|
|
config,
|
|
|
- listeners: RwLock::new(HashMap::new()),
|
|
|
+ senders: RwLock::new(HashMap::new()),
|
|
|
+ sender_index: AtomicUsize::new(0),
|
|
|
+ subscriptions: RwLock::new(HashMap::new()),
|
|
|
})
|
|
|
}
|
|
|
|
|
|
- /// Creates a new subscription for future transactions
|
|
|
+ /// Subscribes to new transactions and revisions with a given filter
|
|
|
pub async fn subscribe(&self, filter: Filter) -> Receiver<Transaction> {
|
|
|
let (sender, receiver) = mpsc::channel(100);
|
|
|
- let mut listeners = self.listeners.write().await;
|
|
|
+ let mut listeners = self.subscriptions.write().await;
|
|
|
let filter = filter.prepare();
|
|
|
|
|
|
+ let sender_index = self
|
|
|
+ .sender_index
|
|
|
+ .fetch_add(1, std::sync::atomic::Ordering::Relaxed);
|
|
|
+
|
|
|
+ self.senders.write().await.insert(sender_index, sender);
|
|
|
+
|
|
|
if let Some(previous_values) = listeners.get_mut(&filter) {
|
|
|
- previous_values.push(sender);
|
|
|
+ previous_values.push(sender_index);
|
|
|
} else {
|
|
|
- listeners.insert(filter, vec![sender]);
|
|
|
+ listeners.insert(filter, vec![sender_index]);
|
|
|
}
|
|
|
|
|
|
receiver
|
|
|
}
|
|
|
|
|
|
+ /// Returns the total number of active subscribers
|
|
|
+ pub async fn subscribers(&self) -> usize {
|
|
|
+ self.senders.read().await.len()
|
|
|
+ }
|
|
|
+
|
|
|
/// The internal usage is to select unspent payments for each account to
|
|
|
/// create new transactions. The external API however does not expose that
|
|
|
/// level of usage, instead it exposes a simple API to move funds using
|
|
@@ -187,24 +206,51 @@ where
|
|
|
async fn persist(&self, mut transaction: Transaction) -> Result<Transaction, Error> {
|
|
|
transaction.persist(&self.config).await?;
|
|
|
|
|
|
- let listeners = self.listeners.read().await;
|
|
|
- for (filter, listeners) in listeners.iter() {
|
|
|
- println!(
|
|
|
- "filter {:#?} -> {:#?} -> {}",
|
|
|
- filter,
|
|
|
- transaction.revision.tags,
|
|
|
- filter.matches(&transaction.transaction, &transaction.revision)
|
|
|
- );
|
|
|
- if filter.matches(&transaction.transaction, &transaction.revision) {
|
|
|
- for listener in listeners.iter() {
|
|
|
- listener
|
|
|
- .send(transaction.clone())
|
|
|
- .await
|
|
|
- .expect("listener failed");
|
|
|
+ {
|
|
|
+ // TODO: Move this block of code to a different thread so spreading messages won't make
|
|
|
+ // creating transaction slower
|
|
|
+ let listeners = self.subscriptions.read().await;
|
|
|
+ let senders = self.senders.read().await;
|
|
|
+
|
|
|
+ let mut subscriptions_to_reindex = vec![];
|
|
|
+ let mut senders_to_remove = vec![];
|
|
|
+
|
|
|
+ for (filter, listeners) in listeners.iter() {
|
|
|
+ if filter.matches(&transaction.transaction, &transaction.revision) {
|
|
|
+ for sender_index in listeners.iter() {
|
|
|
+ if let Some(Err(TrySendError::Closed(_))) = senders
|
|
|
+ .get(sender_index)
|
|
|
+ .map(|sender| sender.try_send(transaction.clone()))
|
|
|
+ {
|
|
|
+ senders_to_remove.push(*sender_index);
|
|
|
+ subscriptions_to_reindex.push(filter.clone());
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
- }
|
|
|
|
|
|
+ drop(listeners);
|
|
|
+ drop(senders);
|
|
|
+
|
|
|
+ if !senders_to_remove.is_empty() {
|
|
|
+ let mut listeners = self.subscriptions.write().await;
|
|
|
+ let mut senders = self.senders.write().await;
|
|
|
+
|
|
|
+ for to_remove in &senders_to_remove {
|
|
|
+ senders.remove(to_remove);
|
|
|
+ }
|
|
|
+
|
|
|
+ for to_rebuild in &subscriptions_to_reindex {
|
|
|
+ if let Some(list_of_senders) = listeners.get_mut(to_rebuild) {
|
|
|
+ *list_of_senders = list_of_senders
|
|
|
+ .into_iter()
|
|
|
+ .filter(|x| senders.contains_key(*x))
|
|
|
+ .map(|x| *x)
|
|
|
+ .collect::<Vec<_>>();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ };
|
|
|
Ok(transaction)
|
|
|
}
|
|
|
|
|
@@ -264,11 +310,13 @@ where
|
|
|
account: &AccountId,
|
|
|
amount: Amount,
|
|
|
status: Status,
|
|
|
+ tags: Vec<Tag>,
|
|
|
reference: String,
|
|
|
) -> Result<Transaction, Error> {
|
|
|
self.persist(Transaction::new_external_deposit(
|
|
|
reference,
|
|
|
status,
|
|
|
+ tags,
|
|
|
vec![(account.clone(), amount)],
|
|
|
)?)
|
|
|
.await
|