11use std:: sync:: Arc ;
2+ use std:: time:: Duration ;
23
34use backon:: { ExponentialBuilder , Retryable } ;
45use katana_primitives:: block:: { BlockNumber , SealedBlockWithStatus } ;
56use katana_primitives:: state:: { StateUpdates , StateUpdatesWithClasses } ;
67use katana_provider:: traits:: block:: BlockWriter ;
78use starknet:: providers:: sequencer:: models:: { BlockId , StateUpdateWithBlock } ;
89use starknet:: providers:: { ProviderError , SequencerGatewayProvider } ;
9- use tracing:: warn;
10+ use tracing:: { debug , warn} ;
1011
1112use super :: { Stage , StageExecutionInput , StageResult } ;
1213
@@ -23,8 +24,13 @@ pub struct Blocks<P> {
2324}
2425
2526impl < P > Blocks < P > {
26- pub fn new ( provider : P , feeder_gateway : SequencerGatewayProvider ) -> Self {
27- Self { provider, downloader : Downloader :: new ( feeder_gateway) }
27+ pub fn new (
28+ provider : P ,
29+ feeder_gateway : SequencerGatewayProvider ,
30+ download_batch_size : usize ,
31+ ) -> Self {
32+ let downloader = Downloader :: new ( feeder_gateway, download_batch_size) ;
33+ Self { provider, downloader }
2834 }
2935}
3036
@@ -36,22 +42,25 @@ impl<P: BlockWriter> Stage for Blocks<P> {
3642
3743 async fn execute ( & mut self , input : & StageExecutionInput ) -> StageResult {
3844 // Download all blocks concurrently
39- let blocks = self . downloader . fetch_blocks_range ( input. from , input. to , 10 ) . await ?;
40-
41- // Then process them sequentially
42- for data in blocks {
43- let StateUpdateWithBlock { state_update, block : fgw_block } = data;
44-
45- let block = SealedBlockWithStatus :: from ( fgw_block) ;
46- let su = StateUpdates :: from ( state_update) ;
47- let su = StateUpdatesWithClasses { state_updates : su, ..Default :: default ( ) } ;
48-
49- let _ = self . provider . insert_block_with_states_and_receipts (
50- block,
51- su,
52- Vec :: new ( ) ,
53- Vec :: new ( ) ,
54- ) ;
45+ let blocks = self . downloader . download_blocks ( input. from , input. to ) . await ?;
46+
47+ if !blocks. is_empty ( ) {
48+ debug ! ( target: "stage" , id = %self . id( ) , total = %blocks. len( ) , "Storing blocks to storage." ) ;
49+ // Store blocks to storage
50+ for block in blocks {
51+ let StateUpdateWithBlock { state_update, block : fgw_block } = block;
52+
53+ let block = SealedBlockWithStatus :: from ( fgw_block) ;
54+ let su = StateUpdates :: from ( state_update) ;
55+ let su = StateUpdatesWithClasses { state_updates : su, ..Default :: default ( ) } ;
56+
57+ let _ = self . provider . insert_block_with_states_and_receipts (
58+ block,
59+ su,
60+ Vec :: new ( ) ,
61+ Vec :: new ( ) ,
62+ ) ;
63+ }
5564 }
5665
5766 Ok ( ( ) )
@@ -60,61 +69,69 @@ impl<P: BlockWriter> Stage for Blocks<P> {
6069
6170#[ derive( Debug , Clone ) ]
6271struct Downloader {
72+ batch_size : usize ,
6373 client : Arc < SequencerGatewayProvider > ,
6474}
6575
6676impl Downloader {
67- fn new ( client : SequencerGatewayProvider ) -> Self {
68- Self { client : Arc :: new ( client) }
77+ fn new ( client : SequencerGatewayProvider , batch_size : usize ) -> Self {
78+ Self { client : Arc :: new ( client) , batch_size }
6979 }
7080
7181 /// Fetch blocks in the range [from, to] in batches of `batch_size`.
72- async fn fetch_blocks_range (
82+ async fn download_blocks (
7383 & self ,
7484 from : BlockNumber ,
7585 to : BlockNumber ,
76- batch_size : usize ,
7786 ) -> Result < Vec < StateUpdateWithBlock > , Error > {
78- let mut all_results = Vec :: with_capacity ( to. saturating_sub ( from) as usize ) ;
79-
80- for batch_start in ( from..=to) . step_by ( batch_size) {
81- let batch_end = ( batch_start + batch_size as u64 - 1 ) . min ( to) ;
82-
83- // fetch in batches and wait on them before proceeding to the next batch
84- let mut futures = Vec :: new ( ) ;
85- for block_num in batch_start..=batch_end {
86- futures. push ( self . fetch_block_with_retry ( block_num) ) ;
87- }
87+ debug ! ( target: "pipeline" , %from, %to, "Downloading blocks." ) ;
88+ let mut blocks = Vec :: with_capacity ( to. saturating_sub ( from) as usize ) ;
8889
89- let batch_results = futures:: future:: join_all ( futures) . await ;
90- all_results. extend ( batch_results) ;
90+ for batch_start in ( from..=to) . step_by ( self . batch_size ) {
91+ let batch_end = ( batch_start + self . batch_size as u64 - 1 ) . min ( to) ;
92+ let batch = self . fetch_blocks_with_retry ( batch_start, batch_end) . await ?;
93+ blocks. extend ( batch) ;
9194 }
9295
93- all_results . into_iter ( ) . collect ( )
96+ Ok ( blocks )
9497 }
9598
96- /// Fetch a single block with the given block number with retry mechanism.
97- async fn fetch_block_with_retry (
99+ /// Fetch blocks with the given block number with retry mechanism at a batch level .
100+ async fn fetch_blocks_with_retry (
98101 & self ,
99- block : BlockNumber ,
100- ) -> Result < StateUpdateWithBlock , Error > {
101- let request = || async move {
102- #[ allow( deprecated) ]
103- self . clone ( ) . fetch_block ( block) . await
104- } ;
102+ from : BlockNumber ,
103+ to : BlockNumber ,
104+ ) -> Result < Vec < StateUpdateWithBlock > , Error > {
105+ let request = || async move { self . clone ( ) . fetch_blocks ( from, to) . await } ;
105106
106107 // Retry only when being rate limited
108+ let backoff = ExponentialBuilder :: default ( ) . with_min_delay ( Duration :: from_secs ( 9 ) ) ;
107109 let result = request
108- . retry ( ExponentialBuilder :: default ( ) )
109- . when ( |e| matches ! ( e, Error :: Gateway ( ProviderError :: RateLimited ) ) )
110+ . retry ( backoff)
110111 . notify ( |error, _| {
111- warn ! ( target: "pipeline" , %block , %error, "Retrying block download." ) ;
112+ warn ! ( target: "pipeline" , %from , %to , %error, "Retrying block download." ) ;
112113 } )
113114 . await ?;
114115
115116 Ok ( result)
116117 }
117118
119+ async fn fetch_blocks (
120+ & self ,
121+ from : BlockNumber ,
122+ to : BlockNumber ,
123+ ) -> Result < Vec < StateUpdateWithBlock > , Error > {
124+ let total = to. saturating_sub ( from) as usize ;
125+ let mut requests = Vec :: with_capacity ( total) ;
126+
127+ for i in from..=to {
128+ requests. push ( self . fetch_block ( i) ) ;
129+ }
130+
131+ let results = futures:: future:: join_all ( requests) . await ;
132+ results. into_iter ( ) . collect ( )
133+ }
134+
118135 /// Fetch a single block with the given block number.
119136 async fn fetch_block ( & self , block : BlockNumber ) -> Result < StateUpdateWithBlock , Error > {
120137 #[ allow( deprecated) ]
@@ -140,7 +157,7 @@ mod tests {
140157 let provider = test_provider ( ) ;
141158 let feeder_gateway = SequencerGatewayProvider :: starknet_alpha_sepolia ( ) ;
142159
143- let mut stage = Blocks :: new ( & provider, feeder_gateway) ;
160+ let mut stage = Blocks :: new ( & provider, feeder_gateway, 10 ) ;
144161
145162 let input = StageExecutionInput { from : from_block, to : to_block } ;
146163 let _ = stage. execute ( & input) . await . expect ( "failed to execute stage" ) ;
0 commit comments