1- use std:: collections:: { BTreeMap , btree_map } ;
1+ use std:: collections:: BTreeMap ;
22use std:: io:: Seek ;
33use std:: sync:: Arc ;
44use std:: time:: Duration ;
@@ -24,12 +24,14 @@ use crate::storage::CoreStorage;
2424#[ serde( default ) ]
2525pub struct ArchiveBlockProviderConfig {
2626 pub max_archive_to_memory_size : ByteSize ,
27+ pub num_prefetched_archives : Option < usize > ,
2728}
2829
2930impl Default for ArchiveBlockProviderConfig {
3031 fn default ( ) -> Self {
3132 Self {
3233 max_archive_to_memory_size : ByteSize :: mb ( 100 ) ,
34+ num_prefetched_archives : Some ( 10 ) ,
3335 }
3436 }
3537}
@@ -48,21 +50,21 @@ impl ArchiveBlockProvider {
4850 ) -> Self {
4951 let proof_checker = ProofChecker :: new ( storage. clone ( ) ) ;
5052
53+ let archives_manager = ArchivesManager :: new ( ) ;
54+
5155 Self {
5256 inner : Arc :: new ( Inner {
5357 client,
5458 proof_checker,
55-
56- known_archives : parking_lot:: Mutex :: new ( Default :: default ( ) ) ,
57-
59+ archives : archives_manager,
5860 storage,
5961 config,
6062 } ) ,
6163 }
6264 }
6365
6466 async fn get_next_block_impl ( & self , block_id : & BlockId ) -> OptionalBlockStuff {
65- let this = self . inner . as_ref ( ) ;
67+ let this = & self . inner ;
6668
6769 let next_mc_seqno = block_id. seqno + 1 ;
6870
@@ -96,7 +98,7 @@ impl ArchiveBlockProvider {
9698 }
9799
98100 async fn get_block_impl ( & self , block_id_relation : & BlockIdRelation ) -> OptionalBlockStuff {
99- let this = self . inner . as_ref ( ) ;
101+ let this = & self . inner ;
100102
101103 let block_id = block_id_relation. block_id ;
102104 let mc_block_id = block_id_relation. mc_block_id ;
@@ -156,23 +158,34 @@ struct Inner {
156158 client : BlockchainRpcClient ,
157159 proof_checker : ProofChecker ,
158160
159- known_archives : parking_lot :: Mutex < ArchivesMap > ,
161+ archives : ArchivesManager ,
160162
161163 config : ArchiveBlockProviderConfig ,
162164}
163165
164166impl Inner {
165- async fn get_archive ( & self , mc_seqno : u32 ) -> Option < ( u32 , ArchiveInfo ) > {
167+ async fn get_archive ( self : & Arc < Self > , mc_seqno : u32 ) -> Option < ( u32 , ArchiveInfo ) > {
166168 loop {
167169 let mut pending = ' pending: {
168- let mut guard = self . known_archives . lock ( ) ;
169-
170170 // Search for the downloaded archive or for and existing downloader task.
171- for ( archive_key, value) in guard . iter ( ) {
171+ for ( archive_key, value) in self . archives . iter ( ) {
172172 match value {
173173 ArchiveSlot :: Downloaded ( info) => {
174174 if info. archive . mc_block_ids . contains_key ( & mc_seqno) {
175- return Some ( ( * archive_key, info. clone ( ) ) ) ;
175+ // Prefetch next archive if enabled
176+ if self . config . num_prefetched_archives . is_some ( ) {
177+ if let Some ( ( last_seqno, _) ) =
178+ info. archive . mc_block_ids . last_key_value ( )
179+ {
180+ let next_archive_start_seqno = last_seqno. saturating_add ( 1 ) ;
181+ let this = self . clone ( ) ;
182+ tokio:: spawn ( async move {
183+ this. try_prefetch_archive ( next_archive_start_seqno)
184+ . await ;
185+ } ) ;
186+ }
187+ }
188+ return Some ( ( archive_key, info. clone ( ) ) ) ;
176189 }
177190 }
178191 ArchiveSlot :: Pending ( task) => break ' pending task. clone ( ) ,
@@ -181,7 +194,8 @@ impl Inner {
181194
182195 // Start downloading otherwise
183196 let task = self . make_downloader ( ) . spawn ( mc_seqno) ;
184- guard. insert ( mc_seqno, ArchiveSlot :: Pending ( task. clone ( ) ) ) ;
197+ self . archives
198+ . insert ( mc_seqno, ArchiveSlot :: Pending ( task. clone ( ) ) ) ;
185199
186200 task
187201 } ;
@@ -205,23 +219,39 @@ impl Inner {
205219 }
206220
207221 // Replace pending with downloaded
208- match self . known_archives . lock ( ) . entry ( pending. archive_key ) {
209- btree_map:: Entry :: Vacant ( _) => {
210- // Do nothing if the entry was already removed.
211- }
212- btree_map:: Entry :: Occupied ( mut entry) => match & res {
222+ match self . archives . get ( & pending. archive_key ) {
223+ Some ( ArchiveSlot :: Pending ( _) ) => match & res {
213224 None => {
214225 // Task was either cancelled or received `TooNew` so no archive received.
215- entry . remove ( ) ;
226+ self . archives . remove ( & pending . archive_key ) ;
216227 }
217228 Some ( info) => {
218229 // Task was finished with a non-empty result so store it.
219- entry. insert ( ArchiveSlot :: Downloaded ( info. clone ( ) ) ) ;
230+ self . archives
231+ . insert ( pending. archive_key , ArchiveSlot :: Downloaded ( info. clone ( ) ) ) ;
220232 }
221233 } ,
234+ _ => {
235+ // Do nothing if the entry was already removed or replaced.
236+ }
222237 }
223238
224239 if finished {
240+ // Prefetch next archive if enabled
241+ if self . config . num_prefetched_archives . is_some ( ) {
242+ if let Some ( info) = res
243+ . as_ref ( )
244+ . and_then ( |i| i. archive . mc_block_ids . last_key_value ( ) )
245+ {
246+ let ( last_seqno, _) = info;
247+ let next_archive_start_seqno = last_seqno. saturating_add ( 1 ) ;
248+ let this = self . clone ( ) ;
249+ // Fire and forget prefetch
250+ tokio:: spawn ( async move {
251+ this. try_prefetch_archive ( next_archive_start_seqno) . await ;
252+ } ) ;
253+ }
254+ }
225255 return res. map ( |info| ( pending. archive_key , info) ) ;
226256 }
227257
@@ -232,28 +262,55 @@ impl Inner {
232262 }
233263
234264 fn remove_archive_if_same ( & self , archive_key : u32 , prev : & ArchiveInfo ) -> bool {
235- match self . known_archives . lock ( ) . entry ( archive_key) {
236- btree_map:: Entry :: Vacant ( _) => false ,
237- btree_map:: Entry :: Occupied ( entry) => {
238- if matches ! (
239- entry. get( ) ,
240- ArchiveSlot :: Downloaded ( info)
241- if Arc :: ptr_eq( & info. archive, & prev. archive)
242- ) {
243- entry. remove ( ) ;
244- true
245- } else {
246- false
265+ match self . archives . get ( & archive_key) {
266+ Some ( ArchiveSlot :: Downloaded ( info) ) if Arc :: ptr_eq ( & info. archive , & prev. archive ) => {
267+ self . archives . remove ( & archive_key) ;
268+ true
269+ }
270+ _ => false ,
271+ }
272+ }
273+
274+ /// Try to prefetch the archive containing `prefetch_seqno` if not already present or pending.
275+ async fn try_prefetch_archive ( & self , prefetch_seqno : u32 ) {
276+ // Check if archive already present or pending
277+ for value in self . archives . values ( ) {
278+ match value {
279+ ArchiveSlot :: Downloaded ( info) => {
280+ if info. archive . mc_block_ids . contains_key ( & prefetch_seqno) {
281+ tracing:: trace!(
282+ prefetch_seqno,
283+ "archive already downloaded, skipping prefetch"
284+ ) ;
285+ return ;
286+ }
287+ }
288+ ArchiveSlot :: Pending ( task) => {
289+ if task. archive_key == prefetch_seqno {
290+ tracing:: trace!(
291+ prefetch_seqno,
292+ "archive download already pending, skipping prefetch"
293+ ) ;
294+ return ;
295+ }
247296 }
248297 }
249298 }
299+
300+ let task = self . make_downloader ( ) . spawn ( prefetch_seqno) ;
301+ self . archives
302+ . insert ( prefetch_seqno, ArchiveSlot :: Pending ( task) ) ;
303+ tracing:: debug!( prefetch_seqno, "starting archive prefetch" ) ;
250304 }
251305
252306 fn make_downloader ( & self ) -> ArchiveDownloader {
253307 ArchiveDownloader {
254308 client : self . client . clone ( ) ,
255309 storage : self . storage . clone ( ) ,
256310 memory_threshold : self . config . max_archive_to_memory_size ,
311+ max_downloaded_archives : self . config . num_prefetched_archives . unwrap_or ( 1 ) , /* 0 will deadlock */
312+ map_len_at_spawn : self . archives . len ( ) ,
313+ archives_len_rx : self . archives . len_update_rx ( ) ,
257314 }
258315 }
259316
@@ -264,8 +321,7 @@ impl Inner {
264321 let mut entries_remaining = 0usize ;
265322 let mut entries_removed = 0usize ;
266323
267- let mut guard = self . known_archives . lock ( ) ;
268- guard. retain ( |_, archive| {
324+ self . archives . retain ( |_, archive| {
269325 let retain;
270326 match archive {
271327 ArchiveSlot :: Downloaded ( info) => match info. archive . mc_block_ids . last_key_value ( ) {
@@ -284,7 +340,6 @@ impl Inner {
284340 entries_removed += !retain as usize ;
285341 retain
286342 } ) ;
287- drop ( guard) ;
288343
289344 tracing:: debug!(
290345 entries_remaining,
@@ -297,6 +352,84 @@ impl Inner {
297352
298353type ArchivesMap = BTreeMap < u32 , ArchiveSlot > ;
299354
355+ struct ArchivesManager {
356+ map : parking_lot:: Mutex < ArchivesMap > ,
357+ len_tx : watch:: Sender < usize > ,
358+ known_archive_len : watch:: Receiver < usize > ,
359+ }
360+
361+ impl ArchivesManager {
362+ fn new ( ) -> Self {
363+ let ( len_tx, len_rx) = watch:: channel ( 0 ) ;
364+ Self {
365+ map : parking_lot:: Mutex :: new ( BTreeMap :: new ( ) ) ,
366+ len_tx,
367+ known_archive_len : len_rx,
368+ }
369+ }
370+
371+ fn len_update_rx ( & self ) -> watch:: Receiver < usize > {
372+ self . known_archive_len . clone ( )
373+ }
374+
375+ fn len ( & self ) -> usize {
376+ self . map . lock ( ) . len ( )
377+ }
378+
379+ fn insert ( & self , key : u32 , value : ArchiveSlot ) -> Option < ArchiveSlot > {
380+ let mut map = self . map . lock ( ) ;
381+ let res = map. insert ( key, value) ;
382+ self . update_len ( map. len ( ) ) ;
383+ res
384+ }
385+
386+ fn remove ( & self , key : & u32 ) -> Option < ArchiveSlot > {
387+ let mut map = self . map . lock ( ) ;
388+ let res = map. remove ( key) ;
389+
390+ self . update_len ( map. len ( ) ) ;
391+
392+ res
393+ }
394+
395+ fn get ( & self , key : & u32 ) -> Option < ArchiveSlot > {
396+ self . map . lock ( ) . get ( key) . cloned ( )
397+ }
398+
399+ fn iter ( & self ) -> Vec < ( u32 , ArchiveSlot ) > {
400+ self . map
401+ . lock ( )
402+ . iter ( )
403+ . map ( |( k, v) | ( * k, v. clone ( ) ) )
404+ . collect ( )
405+ }
406+
407+ fn values ( & self ) -> Vec < ArchiveSlot > {
408+ self . map . lock ( ) . values ( ) . cloned ( ) . collect ( )
409+ }
410+
411+ fn retain < F > ( & self , mut f : F )
412+ where
413+ F : FnMut ( & u32 , & mut ArchiveSlot ) -> bool ,
414+ {
415+ let mut map = self . map . lock ( ) ;
416+ map. retain ( |k, v| f ( k, v) ) ;
417+ self . update_len ( map. len ( ) ) ;
418+ }
419+
420+ fn update_len ( & self , len : usize ) {
421+ let _ = self . len_tx . send_if_modified ( |current| {
422+ if * current != len {
423+ * current = len;
424+ true
425+ } else {
426+ false
427+ }
428+ } ) ;
429+ }
430+ }
431+
432+ #[ derive( Clone ) ]
300433enum ArchiveSlot {
301434 Downloaded ( ArchiveInfo ) ,
302435 Pending ( ArchiveTask ) ,
@@ -312,10 +445,13 @@ struct ArchiveDownloader {
312445 client : BlockchainRpcClient ,
313446 storage : CoreStorage ,
314447 memory_threshold : ByteSize ,
448+ max_downloaded_archives : usize ,
449+ map_len_at_spawn : usize ,
450+ archives_len_rx : watch:: Receiver < usize > ,
315451}
316452
317453impl ArchiveDownloader {
318- fn spawn ( self , mc_seqno : u32 ) -> ArchiveTask {
454+ fn spawn ( mut self , mc_seqno : u32 ) -> ArchiveTask {
319455 // TODO: Use a proper backoff here?
320456 const INTERVAL : Duration = Duration :: from_secs ( 1 ) ;
321457
@@ -337,6 +473,27 @@ impl ArchiveDownloader {
337473 tracing:: debug!( mc_seqno, "finished preloading archive" ) ;
338474 }
339475
476+ if self . map_len_at_spawn > self . max_downloaded_archives {
477+ tracing:: debug!( mc_seqno, "too many archives already downloaded, waiting" ) ;
478+ match self
479+ . archives_len_rx
480+ . wait_for ( |x| x < & self . max_downloaded_archives )
481+ . await
482+ {
483+ Err ( e) => {
484+ tracing:: warn!(
485+ mc_seqno,
486+ "archive task cancelled while waiting for free space: {e}"
487+ ) ;
488+ return ;
489+ }
490+ Ok ( v) => {
491+ let v = * v;
492+ tracing:: debug!( mc_seqno, used_slots = v, "free space available: {v}" ) ;
493+ }
494+ }
495+ }
496+
340497 loop {
341498 match self . try_download ( mc_seqno) . await {
342499 Ok ( res) => {
0 commit comments