@@ -13,7 +13,8 @@ use std::time::Duration;
1313
1414use anyhow:: Result ;
1515use backon:: { BackoffBuilder , ExponentialBuilder } ;
16- use tracing:: trace;
16+ use futures:: future:: join_all;
17+ use tracing:: { trace, warn} ;
1718
1819/// A batch downloader that processes multiple download requests with retry logic.
1920///
@@ -51,9 +52,9 @@ impl<D> BatchDownloader<D> {
5152 ///
5253 /// Uses exponential backoff as the default retry strategy with the following parameters:
5354 /// - Minimum delay: 3 seconds
54- /// - Maximum delay: 60 seconds
55+ /// - Maximum delay: 1 minute
5556 /// - Backoff factor: 2.0 (delays double each retry)
56- /// - Maximum retry attempts: 3
57+ /// - Maximum retry attempts: no limit
5758 ///
5859 /// This means failed downloads will be retried with delays of approximately 3s, 6s, and 12s
5960 /// before giving up (total of 4 attempts including the initial request).
@@ -63,7 +64,11 @@ impl<D> BatchDownloader<D> {
6364 /// * `downloader` - The downloader implementation to use for individual downloads
6465 /// * `batch_size` - Maximum number of items to download concurrently in each batch
6566 pub fn new ( downloader : D , batch_size : usize ) -> Self {
66- let backoff = ExponentialBuilder :: default ( ) . with_min_delay ( Duration :: from_secs ( 3 ) ) ;
67+ let backoff = ExponentialBuilder :: default ( )
68+ . with_min_delay ( Duration :: from_secs ( 3 ) )
69+ . with_max_delay ( Duration :: from_secs ( 60 ) )
70+ . without_max_times ( ) ;
71+
6772 Self { backoff, downloader, batch_size }
6873 }
6974
@@ -137,22 +142,50 @@ where
137142 /// assert_eq!(values.len(), 15);
138143 /// ```
139144 pub async fn download ( & self , keys : Vec < D :: Key > ) -> Result < Vec < D :: Value > , D :: Error > {
145+ let total_items = keys. len ( ) ;
146+ let total_batches = total_items. div_ceil ( self . batch_size ) ;
147+
148+ trace ! (
149+ total_items = %total_items,
150+ batch_size = %self . batch_size,
151+ total_batches = %total_batches,
152+ "Starting batch download."
153+ ) ;
154+
140155 let mut items = Vec :: with_capacity ( keys. len ( ) ) ;
141156
142- for chunk in keys. chunks ( self . batch_size ) {
157+ for ( batch_idx, chunk) in keys. chunks ( self . batch_size ) . enumerate ( ) {
158+ let batch_num = batch_idx + 1 ;
159+ trace ! (
160+ batch = %batch_num,
161+ total_batches = %total_batches,
162+ batch_size = %chunk. len( ) ,
163+ "Processing batch."
164+ ) ;
165+
143166 let batch = self . download_batch_with_retry ( chunk. to_vec ( ) ) . await ?;
144167 items. extend ( batch) ;
168+
169+ trace ! (
170+ batch = %batch_num,
171+ total_batches = %total_batches,
172+ downloaded = %items. len( ) ,
173+ total = %total_items,
174+ "Completed batch."
175+ ) ;
145176 }
146177
178+ trace ! (
179+ total_items = %total_items,
180+ total_batches = %total_batches,
181+ "Batch download completed successfully."
182+ ) ;
183+
147184 Ok ( items)
148185 }
149186
150187 async fn download_batch ( & self , keys : & [ D :: Key ] ) -> Vec < DownloaderResult < D :: Value , D :: Error > > {
151- let mut requests = Vec :: with_capacity ( keys. len ( ) ) ;
152- for key in keys {
153- requests. push ( self . downloader . download ( key) ) ;
154- }
155- futures:: future:: join_all ( requests) . await
188+ join_all ( keys. iter ( ) . map ( |key| self . downloader . download ( key) ) ) . await
156189 }
157190
158191 async fn download_batch_with_retry (
@@ -163,6 +196,7 @@ where
163196
164197 let mut remaining_keys = keys. clone ( ) ;
165198 let mut backoff = self . backoff . clone ( ) . build ( ) ;
199+ let mut retry_attempt = 0 ;
166200
167201 loop {
168202 let batch_result = self . download_batch ( & remaining_keys) . await ;
@@ -198,8 +232,15 @@ where
198232
199233 // Check if we should retry
200234 if let Some ( delay) = backoff. next ( ) {
235+ retry_attempt += 1 ;
201236 if let Some ( ref error) = last_error {
202- trace ! ( %error, failed_keys = %failed_keys. len( ) , "Retrying download for failed keys." ) ;
237+ warn ! (
238+ %error,
239+ failed_keys = %failed_keys. len( ) ,
240+ retry_attempt = %retry_attempt,
241+ delay_secs = %delay. as_secs( ) ,
242+ "Retrying downloads."
243+ ) ;
203244 }
204245
205246 tokio:: time:: sleep ( delay) . await ;
0 commit comments