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 }
43 Some(spill) => {
44 tracing::debug!(new_spill_price = %new_path.price, old_spill_price = %spill.price, "new path is worse than spill path, ignore");
45 }
46 None => {
47 tracing::debug!(new_spill_price = %new_path.price, "new path is a suitable spill path, updating cache");
48 self.spill = Some(new_path);
49 }
50 }
51 }
52}
53
54impl<S: StateRead + 'static> From<Path<S>> for PathEntry<S> {
55 fn from(path: Path<S>) -> Self {
56 Self {
57 path,
58 active: true,
59 spill: None,
60 }
61 }
62}
63
64pub(super) struct PathCache<S: StateRead + 'static>(pub(super) BTreeMap<asset::Id, PathEntry<S>>);
65pub(super) type SharedPathCache<S> = Arc<Mutex<PathCache<S>>>;
66
67impl<S: StateRead + 'static> PathCache<S> {
68 pub fn begin(start: asset::Id, state: StateDelta<S>) -> SharedPathCache<S> {
70 let mut cache = BTreeMap::new();
71 cache.insert(
72 start,
73 PathEntry {
74 path: Path::begin(start, state),
75 active: true,
76 spill: None,
77 },
78 );
79 Arc::new(Mutex::new(Self(cache)))
80 }
81
82 pub fn consider(&mut self, path: Path<S>) {
84 use std::collections::btree_map::Entry;
87 let span = path.span.clone();
88 span.in_scope(|| match self.0.entry(*path.end()) {
89 Entry::Occupied(mut entry) => {
90 entry.get_mut().update(path);
91 }
92 Entry::Vacant(entry) => {
93 tracing::debug!("inserting new path");
94 entry.insert(path.into());
95 }
96 })
97 }
98
99 pub fn extract_active(&mut self) -> Vec<Path<S>> {
101 self.0
102 .iter_mut()
103 .filter_map(|(_, entry)| {
104 if entry.active {
105 entry.active = false;
106 Some(entry.path.fork())
107 } else {
108 None
109 }
110 })
111 .collect()
112 }
113}