subspace_farmer/farmer_cache/
piece_cache_state.rs1use crate::farmer_cache::{CacheBackend, CacheIndex, FarmerCacheOffset};
2use std::collections::btree_map::Values;
3use std::collections::{BTreeMap, HashMap, VecDeque};
4use subspace_core_primitives::pieces::PieceIndex;
5use subspace_networking::libp2p::PeerId;
6use subspace_networking::utils::multihash::ToMultihash;
7use subspace_networking::KeyWithDistance;
8use tracing::{debug, trace};
9
10#[derive(Debug, Default, Clone)]
11pub(super) struct PieceCachesState {
12 stored_pieces: BTreeMap<KeyWithDistance, FarmerCacheOffset>,
13 dangling_free_offsets: VecDeque<FarmerCacheOffset>,
14 backends: Vec<CacheBackend>,
15}
16
17impl PieceCachesState {
18 pub(super) fn new(
19 stored_pieces: BTreeMap<KeyWithDistance, FarmerCacheOffset>,
20 dangling_free_offsets: VecDeque<FarmerCacheOffset>,
21 backends: Vec<CacheBackend>,
22 ) -> Self {
23 Self {
24 stored_pieces,
25 dangling_free_offsets,
26 backends,
27 }
28 }
29
30 pub(super) fn total_capacity(&self) -> usize {
31 self.backends()
32 .fold(0usize, |acc, backend| acc + backend.total_capacity as usize)
33 }
34
35 pub(super) fn pop_free_offset(&mut self) -> Option<FarmerCacheOffset> {
36 match self.dangling_free_offsets.pop_front() {
37 Some(free_offset) => {
38 debug!(?free_offset, "Popped dangling free offset");
39 Some(free_offset)
40 }
41 None => {
42 let mut sorted_backends = self
47 .backends
48 .iter_mut()
49 .enumerate()
50 .filter_map(|(cache_index, backend)| {
51 Some((CacheIndex::try_from(cache_index).ok()?, backend))
52 })
53 .collect::<Vec<_>>();
54 sorted_backends.sort_unstable_by_key(|(_, backend)| backend.free_size());
55 sorted_backends
56 .into_iter()
57 .rev()
58 .find_map(|(cache_index, backend)| {
59 backend
60 .next_free()
61 .map(|free_offset| FarmerCacheOffset::new(cache_index, free_offset))
62 })
63 }
64 }
65 }
66
67 pub(super) fn get_stored_piece(&self, key: &KeyWithDistance) -> Option<&FarmerCacheOffset> {
68 self.stored_pieces.get(key)
69 }
70
71 pub(super) fn contains_stored_piece(&self, key: &KeyWithDistance) -> bool {
72 self.stored_pieces.contains_key(key)
73 }
74
75 pub(super) fn push_stored_piece(
76 &mut self,
77 key: KeyWithDistance,
78 cache_offset: FarmerCacheOffset,
79 ) -> Option<FarmerCacheOffset> {
80 self.stored_pieces.insert(key, cache_offset)
81 }
82
83 pub(super) fn stored_pieces_offsets(&self) -> Values<'_, KeyWithDistance, FarmerCacheOffset> {
84 self.stored_pieces.values()
85 }
86
87 pub(super) fn remove_stored_piece(
88 &mut self,
89 key: &KeyWithDistance,
90 ) -> Option<FarmerCacheOffset> {
91 self.stored_pieces.remove(key)
92 }
93
94 pub(super) fn free_unneeded_stored_pieces(
95 &mut self,
96 piece_indices_to_store: &mut HashMap<KeyWithDistance, PieceIndex>,
97 ) {
98 self.stored_pieces
99 .extract_if(|key, _offset| piece_indices_to_store.remove(key).is_none())
100 .for_each(|(_piece_index, offset)| {
101 self.dangling_free_offsets.push_back(offset);
104 })
105 }
106
107 pub(super) fn push_dangling_free_offset(&mut self, offset: FarmerCacheOffset) {
108 trace!(?offset, "Pushing dangling free offset");
109 self.dangling_free_offsets.push_back(offset);
110 }
111
112 pub(super) fn get_backend(&self, cache_index: CacheIndex) -> Option<&CacheBackend> {
113 self.backends.get(usize::from(cache_index))
114 }
115
116 pub(super) fn backends(&self) -> impl ExactSizeIterator<Item = &CacheBackend> {
117 self.backends.iter()
118 }
119
120 pub(super) fn reuse(
121 self,
122 ) -> (
123 BTreeMap<KeyWithDistance, FarmerCacheOffset>,
124 VecDeque<FarmerCacheOffset>,
125 ) {
126 let Self {
127 mut stored_pieces,
128 mut dangling_free_offsets,
129 backends: _,
130 } = self;
131
132 stored_pieces.clear();
133 dangling_free_offsets.clear();
134 (stored_pieces, dangling_free_offsets)
135 }
136
137 pub(super) fn should_replace(
138 &mut self,
139 key: &KeyWithDistance,
140 ) -> Option<(KeyWithDistance, FarmerCacheOffset)> {
141 if !self.should_include_key_internal(key) {
142 return None;
143 }
144
145 if !self.has_free_capacity() {
146 self.stored_pieces.pop_last()
147 } else {
148 None
149 }
150 }
151
152 pub(super) fn should_include_key(&self, peer_id: PeerId, key: PieceIndex) -> bool {
153 let key = KeyWithDistance::new(peer_id, key.to_multihash());
154 self.should_include_key_internal(&key)
155 }
156
157 fn has_free_capacity(&self) -> bool {
158 if !self.dangling_free_offsets.is_empty() {
159 return true;
160 }
161
162 self.backends.iter().any(|backend| backend.free_size() > 0)
163 }
164
165 fn should_include_key_internal(&self, key: &KeyWithDistance) -> bool {
166 if self.stored_pieces.contains_key(key) {
167 return false;
168 }
169
170 if self.has_free_capacity() {
171 return true;
172 }
173
174 let top_key = self.stored_pieces.last_key_value().map(|(key, _)| key);
175
176 if let Some(top_key) = top_key {
177 top_key > key
178 } else {
179 false
180 }
181 }
182}