penumbra_sdk_dex/component/router/
path_cache.rs1use std::{collections::BTreeMap, sync::Arc};
2
3use cnidarium::{StateDelta, StateRead};
4use parking_lot::Mutex;
5use penumbra_sdk_asset::asset;
6
7use super::Path;
8
9pub(super) struct PathEntry<S: StateRead + 'static> {
11 pub path: Path<S>,
13 pub active: bool,
18 pub spill: Option<Path<S>>,
20}
21
22impl<S: StateRead + 'static> PathEntry<S> {
23 pub fn update(&mut self, new_path: Path<S>) {
25 if new_path < self.path {
26 tracing::debug!(new_price = %new_path.price, old_price = %self.path.price, "new path is better than best path, updating cache");
27 self.spill = Some(std::mem::replace(&mut self.path, new_path));
28 self.active = true;
29 } else {
30 self.update_spill(new_path);
32 }
33 }
34
35 fn update_spill(&mut self, new_path: Path<S>) {
38 match &self.spill {
39 Some(spill) if new_path.price < spill.price => {
40 tracing::debug!(new_spill_price = %new_path.price, old_spill_price = %spill.price, "new path is better than spill path, updating cache");
41 self.spill = Some(new_path);
42 self.active = true;
43 }
44 Some(spill) => {
45 tracing::debug!(new_spill_price = %new_path.price, old_spill_price = %spill.price, "new path is worse than spill path, ignore");
46 }
47 None => {
48 tracing::debug!(new_spill_price = %new_path.price, "new path is a suitable spill path, updating cache");
49 self.spill = Some(new_path);
50 self.active = true;
51 }
52 }
53 }
54}
55
56impl<S: StateRead + 'static> From<Path<S>> for PathEntry<S> {
57 fn from(path: Path<S>) -> Self {
58 Self {
59 path,
60 active: true,
61 spill: None,
62 }
63 }
64}
65
66pub(super) struct PathCache<S: StateRead + 'static>(pub(super) BTreeMap<asset::Id, PathEntry<S>>);
67pub(super) type SharedPathCache<S> = Arc<Mutex<PathCache<S>>>;
68
69impl<S: StateRead + 'static> PathCache<S> {
70 pub fn begin(start: asset::Id, state: StateDelta<S>) -> SharedPathCache<S> {
72 let mut cache = BTreeMap::new();
73 cache.insert(
74 start,
75 PathEntry {
76 path: Path::begin(start, state),
77 active: true,
78 spill: None,
79 },
80 );
81 Arc::new(Mutex::new(Self(cache)))
82 }
83
84 pub fn consider(&mut self, path: Path<S>) {
86 use std::collections::btree_map::Entry;
89 let span = path.span.clone();
90 span.in_scope(|| match self.0.entry(*path.end()) {
91 Entry::Occupied(mut entry) => {
92 entry.get_mut().update(path);
93 }
94 Entry::Vacant(entry) => {
95 tracing::debug!("inserting new path");
96 entry.insert(path.into());
97 }
98 })
99 }
100
101 pub fn extract_active(&mut self) -> Vec<Path<S>> {
103 self.0
104 .iter_mut()
105 .filter_map(|(_, entry)| {
106 if entry.active {
107 entry.active = false;
108 Some(entry.path.fork())
109 } else {
110 None
111 }
112 })
113 .collect()
114 }
115}