ab_data_retrieval/
segment_downloading.rs1use crate::piece_getter::PieceGetter;
4use ab_archiving::archiver::Segment;
5use ab_archiving::reconstructor::{Reconstructor, ReconstructorError};
6use ab_core_primitives::pieces::Piece;
7use ab_core_primitives::segments::{ArchivedHistorySegment, RecordedHistorySegment, SegmentIndex};
8use ab_erasure_coding::ErasureCoding;
9use futures::StreamExt;
10use std::time::Duration;
11use tokio::task::spawn_blocking;
12use tokio::time::sleep;
13use tracing::debug;
14
15pub const SEGMENT_DOWNLOAD_RETRIES: usize = 3;
18
19pub const SEGMENT_DOWNLOAD_RETRY_DELAY: Duration = Duration::from_secs(10);
22
23#[derive(Debug, thiserror::Error)]
25pub enum SegmentDownloadingError {
26 #[error(
28 "Not enough ({downloaded_pieces}/{}) pieces for segment {segment_index}",
29 RecordedHistorySegment::NUM_RAW_RECORDS
30 )]
31 NotEnoughPieces {
32 segment_index: SegmentIndex,
34 downloaded_pieces: usize,
36 },
37
38 #[error("Piece getter error: {source}")]
40 PieceGetterError {
41 #[from]
42 source: anyhow::Error,
43 },
44
45 #[error("Segment reconstruction error: {source}")]
47 SegmentReconstruction {
48 #[from]
49 source: ReconstructorError,
50 },
51
52 #[error("Segment data decoding error: {source}")]
54 SegmentDecoding {
55 #[from]
56 source: parity_scale_codec::Error,
57 },
58}
59
60pub async fn download_segment<PG>(
62 segment_index: SegmentIndex,
63 piece_getter: &PG,
64 erasure_coding: ErasureCoding,
65 retries: usize,
66 retry_delay: Option<Duration>,
67) -> Result<Segment, SegmentDownloadingError>
68where
69 PG: PieceGetter,
70{
71 let reconstructor = Reconstructor::new(erasure_coding);
72
73 let segment_pieces =
74 download_segment_pieces(segment_index, piece_getter, retries, retry_delay).await?;
75
76 let segment = spawn_blocking(move || reconstructor.reconstruct_segment(&segment_pieces))
77 .await
78 .expect("Panic if blocking task panicked")?;
79
80 Ok(segment)
81}
82
83pub async fn download_segment_pieces<PG>(
88 segment_index: SegmentIndex,
89 piece_getter: &PG,
90 retries: usize,
91 retry_delay: Option<Duration>,
92) -> Result<Vec<Option<Piece>>, SegmentDownloadingError>
93where
94 PG: PieceGetter,
95{
96 let mut existing_pieces = [const { None }; ArchivedHistorySegment::NUM_PIECES];
97
98 for retry in 0..=retries {
99 match download_missing_segment_pieces(segment_index, piece_getter, existing_pieces).await {
100 Ok(segment_pieces) => return Ok(segment_pieces),
101 Err(error) => {
102 let (error, incomplete_segment_pieces) = *error;
103 existing_pieces = incomplete_segment_pieces;
104
105 if retry < retries {
106 debug!(
107 %segment_index,
108 %retry,
109 ?retry_delay,
110 ?error,
111 "Failed to download segment pieces once, retrying"
112 );
113 if let Some(retry_delay) = retry_delay {
114 sleep(retry_delay).await;
116 }
117 }
118 }
119 }
120 }
121
122 debug!(
123 %segment_index,
124 %retries,
125 "Failed to download segment pieces"
126 );
127
128 Err(SegmentDownloadingError::NotEnoughPieces {
129 segment_index,
130 downloaded_pieces: existing_pieces
131 .iter()
132 .filter(|piece| piece.is_some())
133 .count(),
134 })
135}
136
137async fn download_missing_segment_pieces<PG>(
144 segment_index: SegmentIndex,
145 piece_getter: &PG,
146 existing_pieces: [Option<Piece>; ArchivedHistorySegment::NUM_PIECES],
147) -> Result<
148 Vec<Option<Piece>>,
149 Box<(
150 SegmentDownloadingError,
151 [Option<Piece>; ArchivedHistorySegment::NUM_PIECES],
152 )>,
153>
154where
155 PG: PieceGetter,
156{
157 let required_pieces_number = RecordedHistorySegment::NUM_RAW_RECORDS;
158 let mut downloaded_pieces = existing_pieces
159 .iter()
160 .filter(|piece| piece.is_some())
161 .count();
162
163 let mut first_success = None;
165 let mut last_success = None;
166 let mut first_failure = None;
167 let mut last_failure = None;
168
169 let mut segment_pieces = existing_pieces;
170
171 let mut pieces_iter = segment_index.segment_piece_indexes().into_iter();
172
173 while !pieces_iter.is_empty() && downloaded_pieces != required_pieces_number {
175 let piece_indices = pieces_iter
176 .by_ref()
177 .filter(|piece_index| segment_pieces[usize::from(piece_index.position())].is_none())
178 .take(required_pieces_number - downloaded_pieces)
179 .collect();
180
181 let mut received_segment_pieces = match piece_getter.get_pieces(piece_indices).await {
182 Ok(pieces) => pieces,
183 Err(error) => return Err(Box::new((error.into(), segment_pieces))),
184 };
185
186 while let Some((piece_index, result)) = received_segment_pieces.next().await {
187 match result {
188 Ok(Some(piece)) => {
189 downloaded_pieces += 1;
190 segment_pieces
191 .get_mut(usize::from(piece_index.position()))
192 .expect("Piece position is by definition within segment; qed")
193 .replace(piece);
194
195 if first_success.is_none() {
196 first_success = Some(piece_index.position());
197 }
198 last_success = Some(piece_index.position());
199 }
200 Ok(None) => {
205 debug!(%piece_index, "Piece was not found");
206 if first_failure.is_none() {
207 first_failure = Some(piece_index.position());
208 }
209 last_failure = Some(piece_index.position());
210 }
211 Err(error) => {
212 debug!(%error, %piece_index, "Failed to get piece");
213 if first_failure.is_none() {
214 first_failure = Some(piece_index.position());
215 }
216 last_failure = Some(piece_index.position());
217 }
218 }
219 }
220 }
221
222 if downloaded_pieces < required_pieces_number {
223 debug!(
224 %segment_index,
225 %downloaded_pieces,
226 %required_pieces_number,
227 ?first_success,
229 ?last_success,
230 ?first_failure,
231 ?last_failure,
232 "Failed to retrieve pieces for segment"
233 );
234
235 return Err(Box::new((
236 SegmentDownloadingError::NotEnoughPieces {
237 segment_index,
238 downloaded_pieces: segment_pieces
239 .iter()
240 .filter(|piece| piece.is_some())
241 .count(),
242 },
243 segment_pieces,
244 )));
245 }
246
247 Ok(segment_pieces.to_vec())
248}