kvbm_logical/manager/
mod.rs1mod builder;
11
12#[cfg(test)]
13mod tests;
14
15pub use builder::{
16 BlockManagerBuilderError, BlockManagerConfigBuilder, BlockManagerResetError,
17 FrequencyTrackingCapacity, InactiveBackendConfig, LineageEviction,
18};
19
20use std::collections::HashMap;
21use std::sync::Arc;
22
23use crate::blocks::{BlockMetadata, CompleteBlock, ImmutableBlock, MutableBlock};
24use crate::metrics::BlockPoolMetrics;
25use crate::pools::{BlockDuplicationPolicy, BlockStore, SequenceHash};
26use crate::registry::BlockRegistry;
27
28pub struct BlockManager<T: BlockMetadata> {
32 pub(crate) store: Arc<BlockStore<T>>,
33 pub(crate) block_registry: BlockRegistry,
34 pub(crate) duplication_policy: BlockDuplicationPolicy,
35 pub(crate) total_blocks: usize,
36 pub(crate) block_size: usize,
37 pub(crate) metrics: Arc<BlockPoolMetrics>,
38}
39
40impl<T: BlockMetadata + Sync> BlockManager<T> {
41 pub fn builder() -> BlockManagerConfigBuilder<T> {
43 BlockManagerConfigBuilder::default()
44 }
45
46 pub fn id(&self) -> crate::ManagerId {
55 self.store.id()
56 }
57
58 pub fn allocate_blocks(&self, count: usize) -> Option<Vec<MutableBlock<T>>> {
63 self.allocate_blocks_with_evictions(count)
64 .map(|(blocks, _evicted)| blocks)
65 }
66
67 pub fn allocate_blocks_with_evictions(
70 &self,
71 count: usize,
72 ) -> Option<(Vec<MutableBlock<T>>, Vec<SequenceHash>)> {
73 self.store.allocate_atomic(count)
74 }
75
76 pub fn reset_inactive_pool(&self) -> Result<(), BlockManagerResetError> {
78 let blocks = self.store.drain_inactive_to_mutable();
79 drop(blocks);
80
81 let reset_count = self.store.reset_len();
82 if reset_count != self.total_blocks {
83 return Err(BlockManagerResetError::BlockCountMismatch {
84 expected: self.total_blocks,
85 actual: reset_count,
86 });
87 }
88
89 Ok(())
90 }
91
92 pub fn register_blocks(&self, blocks: Vec<CompleteBlock<T>>) -> Vec<ImmutableBlock<T>> {
94 if blocks.is_empty() {
95 return Vec::new();
96 }
97
98 let handles = self
99 .block_registry
100 .register_sequence_hashes(blocks.iter().map(CompleteBlock::sequence_hash));
101 let batch_size = blocks.len();
102 let registered =
103 self.store
104 .register_completed_blocks(blocks, handles, self.duplication_policy);
105 self.metrics
109 .inc_registrations_by(u64::try_from(batch_size).unwrap_or(u64::MAX));
110 registered
111 .into_iter()
112 .map(ImmutableBlock::from_inner)
113 .collect()
114 }
115
116 pub fn register_block(&self, block: CompleteBlock<T>) -> ImmutableBlock<T> {
118 let handle = self
119 .block_registry
120 .register_sequence_hash(block.sequence_hash());
121 let inner = handle.register_block(block, self.duplication_policy, &self.store);
122 self.metrics.inc_registrations();
123 ImmutableBlock::from_inner(inner)
124 }
125
126 pub fn match_blocks(&self, seq_hash: &[SequenceHash]) -> Vec<ImmutableBlock<T>> {
136 self.metrics
137 .inc_match_hashes_requested(seq_hash.len() as u64);
138
139 if seq_hash.is_empty() {
140 self.metrics.inc_match_blocks_returned(0);
141 return Vec::new();
142 }
143
144 let inners = self.store.match_prefix_locked_batch(seq_hash);
146
147 if self.block_registry.has_frequency_tracking() {
152 for inner in &inners {
153 self.block_registry.touch(inner.sequence_hash());
154 }
155 }
156
157 let matched: Vec<ImmutableBlock<T>> =
158 inners.into_iter().map(ImmutableBlock::from_inner).collect();
159
160 self.metrics.inc_match_blocks_returned(matched.len() as u64);
161 tracing::debug!(
162 num_hashes = seq_hash.len(),
163 total_matched = matched.len(),
164 "match_blocks result"
165 );
166 tracing::trace!(matched = ?matched, "matched blocks");
167 matched
168 }
169
170 pub fn match_blocks_scattered(
183 &self,
184 seq_hash: &[SequenceHash],
185 ) -> Vec<Option<ImmutableBlock<T>>> {
186 self.metrics
187 .inc_match_hashes_requested(seq_hash.len() as u64);
188
189 if seq_hash.is_empty() {
190 self.metrics.inc_match_blocks_returned(0);
191 return Vec::new();
192 }
193
194 let inners = self.store.match_scattered_locked_batch(seq_hash);
197
198 if self.block_registry.has_frequency_tracking() {
201 for inner in inners.iter().flatten() {
202 self.block_registry.touch(inner.sequence_hash());
203 }
204 }
205
206 let hit_count = inners.iter().filter(|inner| inner.is_some()).count();
207 let matched = inners
208 .into_iter()
209 .map(|inner| inner.map(ImmutableBlock::from_inner))
210 .collect();
211
212 self.metrics.inc_match_blocks_returned(hit_count as u64);
213 tracing::debug!(
214 num_hashes = seq_hash.len(),
215 total_matched = hit_count,
216 "match_blocks_scattered result"
217 );
218 matched
219 }
220
221 pub fn scan_matches(
225 &self,
226 seq_hashes: &[SequenceHash],
227 touch: bool,
228 ) -> HashMap<SequenceHash, ImmutableBlock<T>> {
229 self.metrics
230 .inc_scan_hashes_requested(seq_hashes.len() as u64);
231
232 let mut result = HashMap::new();
233
234 let active_found = self.scan_active_matches(seq_hashes, touch);
235 for (hash, inner) in active_found {
236 result.insert(hash, ImmutableBlock::from_inner(inner));
237 }
238
239 let remaining: Vec<SequenceHash> = seq_hashes
240 .iter()
241 .filter(|h| !result.contains_key(h))
242 .copied()
243 .collect();
244
245 if !remaining.is_empty() {
246 let inactive_found = self.store.scan_inactive_primaries(&remaining, touch);
247 for (hash, inner) in inactive_found {
248 result.insert(hash, ImmutableBlock::from_inner(inner));
249 }
250 }
251
252 self.metrics.inc_scan_blocks_returned(result.len() as u64);
253
254 result
255 }
256
257 fn scan_active_matches(
260 &self,
261 hashes: &[SequenceHash],
262 touch: bool,
263 ) -> Vec<(SequenceHash, Arc<crate::blocks::ImmutableBlockInner<T>>)> {
264 hashes
265 .iter()
266 .filter_map(|hash| {
267 self.block_registry
268 .match_sequence_hash(*hash, touch)
269 .and_then(|handle| {
270 handle
271 .try_get_inner::<T>(&self.store, touch)
272 .map(|inner| (*hash, inner))
273 })
274 })
275 .collect()
276 }
277
278 pub fn total_blocks(&self) -> usize {
280 self.total_blocks
281 }
282
283 pub fn available_blocks(&self) -> usize {
289 self.store.available_len()
290 }
291
292 pub fn block_size(&self) -> usize {
294 self.block_size
295 }
296
297 pub fn duplication_policy(&self) -> &BlockDuplicationPolicy {
299 &self.duplication_policy
300 }
301
302 pub fn block_registry(&self) -> &BlockRegistry {
304 &self.block_registry
305 }
306
307 pub fn metrics(&self) -> &Arc<BlockPoolMetrics> {
309 &self.metrics
310 }
311
312 #[cfg(test)]
316 pub(crate) fn store_for_test(&self) -> &Arc<BlockStore<T>> {
317 &self.store
318 }
319}