1use crate::farm::{SectorExpirationDetails, SectorPlottingDetails, SectorUpdate};
2use crate::node_client::NodeClient;
3use crate::plotter::{Plotter, SectorPlottingProgress};
4use crate::single_disk_farm::direct_io_file::DirectIoFile;
5use crate::single_disk_farm::metrics::{SectorState, SingleDiskFarmMetrics};
6use crate::single_disk_farm::{
7 BackgroundTaskError, Handlers, PlotMetadataHeader, RESERVED_PLOT_METADATA,
8};
9use async_lock::{Mutex as AsyncMutex, RwLock as AsyncRwLock, Semaphore, SemaphoreGuard};
10use futures::channel::{mpsc, oneshot};
11use futures::stream::FuturesOrdered;
12use futures::{FutureExt, SinkExt, StreamExt, select};
13use parity_scale_codec::Encode;
14use rand::prelude::*;
15use std::collections::HashSet;
16use std::future::Future;
17use std::io;
18use std::num::NonZeroUsize;
19use std::ops::Range;
20use std::pin::pin;
21use std::sync::Arc;
22use std::time::{Duration, Instant};
23use subspace_core_primitives::PublicKey;
24use subspace_core_primitives::hashes::Blake3Hash;
25use subspace_core_primitives::pieces::PieceOffset;
26use subspace_core_primitives::sectors::{SectorId, SectorIndex};
27use subspace_core_primitives::segments::{HistorySize, SegmentHeader, SegmentIndex};
28use subspace_farmer_components::file_ext::FileExt;
29use subspace_farmer_components::plotting::PlottedSector;
30use subspace_farmer_components::sector::SectorMetadataChecksummed;
31use thiserror::Error;
32use tokio::sync::watch;
33use tokio::task;
34use tracing::{Instrument, debug, info, info_span, trace, warn};
35
36const FARMER_APP_INFO_RETRY_INTERVAL: Duration = Duration::from_millis(500);
37const PLOTTING_RETRY_DELAY: Duration = Duration::from_secs(1);
38
39pub(super) struct SectorToPlot {
40 sector_index: SectorIndex,
41 progress: f32,
43 last_queued: bool,
45 acknowledgement_sender: oneshot::Sender<()>,
46}
47
48#[derive(Debug, Error)]
50pub enum PlottingError {
51 #[error("Failed to retrieve farmer info: {error}")]
53 FailedToGetFarmerInfo {
54 error: anyhow::Error,
56 },
57 #[error("Failed to get segment header: {error}")]
59 FailedToGetSegmentHeader {
60 error: anyhow::Error,
62 },
63 #[error("Missing archived segment header: {segment_index}")]
65 MissingArchivedSegmentHeader {
66 segment_index: SegmentIndex,
68 },
69 #[error("Failed to subscribe to archived segments: {error}")]
71 FailedToSubscribeArchivedSegments {
72 error: anyhow::Error,
74 },
75 #[error("Low-level plotting error: {0}")]
77 LowLevel(String),
78 #[error("Plotting I/O error: {0}")]
80 Io(#[from] io::Error),
81 #[error("Background downloading panicked")]
83 BackgroundDownloadingPanicked,
84}
85
86pub(super) struct SectorPlottingOptions<'a, NC> {
87 pub(super) public_key: PublicKey,
88 pub(super) node_client: &'a NC,
89 pub(super) pieces_in_sector: u16,
90 pub(super) sector_size: usize,
91 pub(super) plot_file: Arc<DirectIoFile>,
92 pub(super) metadata_file: Arc<DirectIoFile>,
93 pub(super) handlers: &'a Handlers,
94 pub(super) global_mutex: &'a AsyncMutex<()>,
95 pub(super) plotter: Arc<dyn Plotter>,
96 pub(super) metrics: Option<Arc<SingleDiskFarmMetrics>>,
97}
98
99pub(super) struct PlottingOptions<'a, NC> {
100 pub(super) metadata_header: PlotMetadataHeader,
101 pub(super) sectors_metadata: &'a AsyncRwLock<Vec<SectorMetadataChecksummed>>,
102 pub(super) sectors_being_modified: &'a AsyncRwLock<HashSet<SectorIndex>>,
103 pub(super) sectors_to_plot_receiver: mpsc::Receiver<SectorToPlot>,
104 pub(super) sector_plotting_options: SectorPlottingOptions<'a, NC>,
105 pub(super) max_plotting_sectors_per_farm: NonZeroUsize,
106}
107
108pub(super) async fn plotting<NC>(
113 plotting_options: PlottingOptions<'_, NC>,
114) -> Result<(), PlottingError>
115where
116 NC: NodeClient,
117{
118 let PlottingOptions {
119 mut metadata_header,
120 sectors_metadata,
121 sectors_being_modified,
122 mut sectors_to_plot_receiver,
123 sector_plotting_options,
124 max_plotting_sectors_per_farm,
125 } = plotting_options;
126
127 let sector_plotting_options = §or_plotting_options;
128 let cutover = metadata_header.cutover;
129 let plotting_semaphore = Semaphore::new(max_plotting_sectors_per_farm.get());
130 let mut sectors_being_plotted = FuturesOrdered::new();
131 let (sector_plotting_result_sender, mut sector_plotting_result_receiver) = mpsc::unbounded();
134 let process_plotting_result_fut = async move {
135 while let Some(sector_plotting_result) = sector_plotting_result_receiver.next().await {
136 process_plotting_result(
137 sector_plotting_result,
138 sectors_metadata,
139 sectors_being_modified,
140 &mut metadata_header,
141 Arc::clone(§or_plotting_options.metadata_file),
142 )
143 .await?;
144 }
145
146 unreachable!(
147 "Stream will not end before the rest of the plotting process is shutting down"
148 );
149 };
150 let process_plotting_result_fut = process_plotting_result_fut.fuse();
151 let mut process_plotting_result_fut = pin!(process_plotting_result_fut);
152
153 loop {
156 select! {
157 maybe_sector_to_plot = sectors_to_plot_receiver.next() => {
158 let Some(sector_to_plot) = maybe_sector_to_plot else {
159 break;
160 };
161
162 let sector_index = sector_to_plot.sector_index;
163 let sector_plotting_init_fut = plot_single_sector(
164 sector_to_plot,
165 sector_plotting_options,
166 sectors_metadata,
167 sectors_being_modified,
168 &plotting_semaphore,
169 cutover,
170 )
171 .instrument(info_span!("", %sector_index))
172 .fuse();
173 let mut sector_plotting_init_fut = pin!(sector_plotting_init_fut);
174
175 loop {
179 select! {
180 sector_plotting_init_result = sector_plotting_init_fut => {
181 let sector_plotting_fut = match sector_plotting_init_result {
182 PlotSingleSectorResult::Scheduled(future) => future,
183 PlotSingleSectorResult::Skipped => {
184 break;
185 }
186 PlotSingleSectorResult::FatalError(error) => {
187 return Err(error);
188 }
189 };
190 sectors_being_plotted.push_back(
191 sector_plotting_fut.instrument(info_span!("", %sector_index))
192 );
193 break;
194 }
195 maybe_sector_plotting_result = sectors_being_plotted.select_next_some() => {
196 sector_plotting_result_sender
197 .unbounded_send(maybe_sector_plotting_result?)
198 .expect("Sending means receiver is not dropped yet; qed");
199 }
200 result = process_plotting_result_fut => {
201 return result;
202 }
203 }
204 }
205 }
206 maybe_sector_plotting_result = sectors_being_plotted.select_next_some() => {
207 sector_plotting_result_sender
208 .unbounded_send(maybe_sector_plotting_result?)
209 .expect("Sending means receiver is not dropped yet; qed");
210 }
211 result = process_plotting_result_fut => {
212 return result;
213 }
214 }
215 }
216
217 Ok(())
218}
219
220async fn process_plotting_result(
221 sector_plotting_result: SectorPlottingResult<'_>,
222 sectors_metadata: &AsyncRwLock<Vec<SectorMetadataChecksummed>>,
223 sectors_being_modified: &AsyncRwLock<HashSet<SectorIndex>>,
224 metadata_header: &mut PlotMetadataHeader,
225 metadata_file: Arc<DirectIoFile>,
226) -> Result<(), PlottingError> {
227 let SectorPlottingResult {
228 sector_metadata,
229 replotting,
230 last_queued,
231 plotting_permit,
232 } = sector_plotting_result;
233
234 let sector_index = sector_metadata.sector_index;
235
236 {
237 let mut sectors_metadata = sectors_metadata.write().await;
238 if let Some(existing_sector_metadata) = sectors_metadata.get_mut(sector_index as usize) {
240 *existing_sector_metadata = sector_metadata;
241 } else {
242 sectors_metadata.push(sector_metadata);
243 }
244 }
245
246 sectors_being_modified.write().await.remove(§or_index);
248
249 if sector_index + 1 > metadata_header.plotted_sector_count {
250 metadata_header.plotted_sector_count = sector_index + 1;
251
252 let encoded_metadata_header = metadata_header.encode();
253 let write_fut =
254 task::spawn_blocking(move || metadata_file.write_all_at(&encoded_metadata_header, 0));
255 write_fut.await.map_err(|error| {
256 PlottingError::LowLevel(format!("Failed to spawn blocking tokio task: {error}"))
257 })??;
258 }
259
260 if last_queued {
261 if replotting {
262 info!("Replotting complete");
263 } else {
264 info!("Initial plotting complete");
265 }
266 }
267
268 drop(plotting_permit);
269
270 Ok(())
271}
272
273enum PlotSingleSectorResult<F> {
274 Scheduled(F),
275 Skipped,
276 FatalError(PlottingError),
277}
278
279struct SectorPlottingResult<'a> {
280 sector_metadata: SectorMetadataChecksummed,
281 replotting: bool,
282 last_queued: bool,
283 plotting_permit: SemaphoreGuard<'a>,
284}
285
286async fn plot_single_sector<'a, NC>(
287 sector_to_plot: SectorToPlot,
288 sector_plotting_options: &'a SectorPlottingOptions<'a, NC>,
289 sectors_metadata: &'a AsyncRwLock<Vec<SectorMetadataChecksummed>>,
290 sectors_being_modified: &'a AsyncRwLock<HashSet<SectorIndex>>,
291 plotting_semaphore: &'a Semaphore,
292 cutover: Option<HistorySize>,
293) -> PlotSingleSectorResult<
294 impl Future<Output = Result<SectorPlottingResult<'a>, PlottingError>> + 'a,
295>
296where
297 NC: NodeClient,
298{
299 let SectorPlottingOptions {
300 public_key,
301 node_client,
302 pieces_in_sector,
303 sector_size,
304 plot_file,
305 metadata_file,
306 handlers,
307 global_mutex,
308 plotter,
309 metrics,
310 } = sector_plotting_options;
311
312 let SectorToPlot {
313 sector_index,
314 progress,
315 last_queued,
316 acknowledgement_sender: _acknowledgement_sender,
317 } = sector_to_plot;
318 trace!("Preparing to plot sector");
319
320 let maybe_old_sector_metadata = sectors_metadata
321 .read()
322 .await
323 .get(sector_index as usize)
324 .cloned();
325 let replotting = maybe_old_sector_metadata.is_some();
326
327 let farmer_app_info = loop {
336 let farmer_app_info = match node_client.farmer_app_info().await {
337 Ok(farmer_app_info) => farmer_app_info,
338 Err(error) => {
339 return PlotSingleSectorResult::FatalError(PlottingError::FailedToGetFarmerInfo {
340 error,
341 });
342 }
343 };
344
345 if let Some(old_sector_metadata) = &maybe_old_sector_metadata
346 && farmer_app_info.protocol_info.history_size <= old_sector_metadata.history_size
347 {
348 if farmer_app_info.protocol_info.min_sector_lifetime == HistorySize::ONE {
349 debug!(
350 current_history_size = %farmer_app_info.protocol_info.history_size,
351 old_sector_history_size = %old_sector_metadata.history_size,
352 "Latest protocol history size is not yet newer than old sector history \
353 size, wait for a bit and try again"
354 );
355 tokio::time::sleep(FARMER_APP_INFO_RETRY_INTERVAL).await;
356 continue;
357 } else {
358 debug!(
359 current_history_size = %farmer_app_info.protocol_info.history_size,
360 old_sector_history_size = %old_sector_metadata.history_size,
361 "Skipped sector plotting, likely redundant due to redundant archived \
362 segment notification"
363 );
364 return PlotSingleSectorResult::Skipped;
365 }
366 }
367
368 if let Some(cutover) = cutover
371 && farmer_app_info.protocol_info.history_size <= cutover
372 {
373 debug!(
374 current_history_size = %farmer_app_info.protocol_info.history_size,
375 %cutover,
376 "History size has not advanced past the proof-of-space cutover yet, waiting"
377 );
378 tokio::time::sleep(FARMER_APP_INFO_RETRY_INTERVAL).await;
379 continue;
380 }
381
382 break farmer_app_info;
383 };
384
385 {
387 let mut sectors_being_modified = sectors_being_modified.write().await;
388 if !sectors_being_modified.insert(sector_index) {
389 debug!("Skipped sector plotting, it is already in progress");
390 return PlotSingleSectorResult::Skipped;
391 }
392 }
393
394 let plotting_permit = plotting_semaphore.acquire().await;
395
396 if let Some(metrics) = metrics {
397 metrics.sector_plotting.inc();
398 }
399 let sector_state = SectorUpdate::Plotting(SectorPlottingDetails::Starting {
400 progress,
401 replotting,
402 last_queued,
403 });
404 handlers
405 .sector_update
406 .call_simple(&(sector_index, sector_state));
407
408 let start = Instant::now();
409
410 let (progress_sender, mut progress_receiver) = mpsc::channel(10);
411
412 plotter
414 .plot_sector(
415 *public_key,
416 sector_index,
417 farmer_app_info.protocol_info,
418 *pieces_in_sector,
419 replotting,
420 progress_sender,
421 )
422 .await;
423
424 if replotting {
425 info!("Replotting sector ({progress:.2}% complete)");
426 } else {
427 info!("Plotting sector ({progress:.2}% complete)");
428 }
429
430 PlotSingleSectorResult::Scheduled(async move {
431 let plotted_sector = loop {
432 match plot_single_sector_internal(
433 sector_index,
434 *sector_size,
435 plot_file,
436 metadata_file,
437 handlers,
438 global_mutex,
439 progress_receiver,
440 metrics,
441 )
442 .await?
443 {
444 Ok(plotted_sector) => {
445 break plotted_sector;
446 }
447 Err(error) => {
448 warn!(
449 %error,
450 "Failed to plot sector, retrying in {PLOTTING_RETRY_DELAY:?}"
451 );
452
453 tokio::time::sleep(PLOTTING_RETRY_DELAY).await;
454 }
455 }
456
457 let (retry_progress_sender, retry_progress_receiver) = mpsc::channel(10);
458 progress_receiver = retry_progress_receiver;
459
460 plotter
462 .plot_sector(
463 *public_key,
464 sector_index,
465 farmer_app_info.protocol_info,
466 *pieces_in_sector,
467 replotting,
468 retry_progress_sender,
469 )
470 .await;
471
472 if replotting {
473 info!("Replotting sector retry");
474 } else {
475 info!("Plotting sector retry");
476 }
477 };
478
479 let maybe_old_plotted_sector = maybe_old_sector_metadata.map(|old_sector_metadata| {
480 let old_history_size = old_sector_metadata.history_size;
481
482 PlottedSector {
483 sector_id: plotted_sector.sector_id,
484 sector_index: plotted_sector.sector_index,
485 sector_metadata: old_sector_metadata,
486 piece_indexes: {
487 let mut piece_indexes = Vec::with_capacity(usize::from(*pieces_in_sector));
488 (PieceOffset::ZERO..)
489 .take(usize::from(*pieces_in_sector))
490 .map(|piece_offset| {
491 plotted_sector.sector_id.derive_piece_index(
492 piece_offset,
493 old_history_size,
494 farmer_app_info.protocol_info.max_pieces_in_sector,
495 farmer_app_info.protocol_info.recent_segments,
496 farmer_app_info.protocol_info.recent_history_fraction,
497 )
498 })
499 .collect_into(&mut piece_indexes);
500 piece_indexes
501 },
502 }
503 });
504
505 if replotting {
506 debug!("Sector replotted successfully");
507 } else {
508 debug!("Sector plotted successfully");
509 }
510
511 let sector_metadata = plotted_sector.sector_metadata.clone();
512
513 let time = start.elapsed();
514 if let Some(metrics) = metrics {
515 metrics.sector_plotting_time.observe(time.as_secs_f64());
516 metrics.sector_plotted.inc();
517 metrics.update_sector_state(SectorState::Plotted);
518 }
519 let sector_state = SectorUpdate::Plotting(SectorPlottingDetails::Finished {
520 plotted_sector,
521 old_plotted_sector: maybe_old_plotted_sector,
522 time,
523 });
524 handlers
525 .sector_update
526 .call_simple(&(sector_index, sector_state));
527
528 Ok(SectorPlottingResult {
529 sector_metadata,
530 replotting,
531 last_queued,
532 plotting_permit,
533 })
534 })
535}
536
537#[allow(clippy::too_many_arguments)]
540async fn plot_single_sector_internal(
541 sector_index: SectorIndex,
542 sector_size: usize,
543 plot_file: &Arc<DirectIoFile>,
544 metadata_file: &Arc<DirectIoFile>,
545 handlers: &Handlers,
546 global_mutex: &AsyncMutex<()>,
547 mut progress_receiver: mpsc::Receiver<SectorPlottingProgress>,
548 metrics: &Option<Arc<SingleDiskFarmMetrics>>,
549) -> Result<Result<PlottedSector, PlottingError>, PlottingError> {
550 let progress_processor_fut = async {
552 while let Some(progress) = progress_receiver.next().await {
553 match progress {
554 SectorPlottingProgress::Downloading => {
555 if let Some(metrics) = metrics {
556 metrics.sector_downloading.inc();
557 }
558 handlers.sector_update.call_simple(&(
559 sector_index,
560 SectorUpdate::Plotting(SectorPlottingDetails::Downloading),
561 ));
562 }
563 SectorPlottingProgress::Downloaded(time) => {
564 if let Some(metrics) = metrics {
565 metrics.sector_downloading_time.observe(time.as_secs_f64());
566 metrics.sector_downloaded.inc();
567 }
568 handlers.sector_update.call_simple(&(
569 sector_index,
570 SectorUpdate::Plotting(SectorPlottingDetails::Downloaded(time)),
571 ));
572 }
573 SectorPlottingProgress::Encoding => {
574 if let Some(metrics) = metrics {
575 metrics.sector_encoding.inc();
576 }
577 handlers.sector_update.call_simple(&(
578 sector_index,
579 SectorUpdate::Plotting(SectorPlottingDetails::Encoding),
580 ));
581 }
582 SectorPlottingProgress::Encoded(time) => {
583 if let Some(metrics) = metrics {
584 metrics.sector_encoding_time.observe(time.as_secs_f64());
585 metrics.sector_encoded.inc();
586 }
587 handlers.sector_update.call_simple(&(
588 sector_index,
589 SectorUpdate::Plotting(SectorPlottingDetails::Encoded(time)),
590 ));
591 }
592 SectorPlottingProgress::Finished {
593 plotted_sector,
594 time: _,
595 sector,
596 } => {
597 return Ok((plotted_sector, sector));
598 }
599 SectorPlottingProgress::Error { error } => {
600 if let Some(metrics) = metrics {
601 metrics.sector_plotting_error.inc();
602 }
603 handlers.sector_update.call_simple(&(
604 sector_index,
605 SectorUpdate::Plotting(SectorPlottingDetails::Error(error.clone())),
606 ));
607 return Err(error);
608 }
609 }
610 }
611
612 Err("Plotting progress stream ended before plotting finished".to_string())
613 };
614
615 let (plotted_sector, mut sector) = match progress_processor_fut.await {
616 Ok(result) => result,
617 Err(error) => {
618 return Ok(Err(PlottingError::LowLevel(error)));
619 }
620 };
621
622 {
623 global_mutex.lock().await;
625
626 if let Some(metrics) = metrics {
627 metrics.sector_writing.inc();
628 }
629 handlers.sector_update.call_simple(&(
630 sector_index,
631 SectorUpdate::Plotting(SectorPlottingDetails::Writing),
632 ));
633
634 let start = Instant::now();
635
636 {
637 let sector_write_base_offset = u64::from(sector_index) * sector_size as u64;
638 let mut total_received = 0;
639 let mut sector_write_offset = sector_write_base_offset;
640 while let Some(maybe_sector_chunk) = sector.next().await {
641 let sector_chunk = match maybe_sector_chunk {
642 Ok(sector_chunk) => sector_chunk,
643 Err(error) => {
644 return Ok(Err(PlottingError::LowLevel(format!(
645 "Sector chunk receive error: {error}"
646 ))));
647 }
648 };
649
650 total_received += sector_chunk.len();
651
652 if total_received > sector_size {
653 return Ok(Err(PlottingError::LowLevel(format!(
654 "Received too many bytes {total_received} instead of expected \
655 {sector_size} bytes"
656 ))));
657 }
658
659 let sector_chunk_size = sector_chunk.len() as u64;
660
661 trace!(sector_chunk_size, "Writing sector chunk to disk");
662 let write_fut = task::spawn_blocking({
663 let plot_file = Arc::clone(plot_file);
664
665 move || plot_file.write_all_at(§or_chunk, sector_write_offset)
666 });
667 write_fut.await.map_err(|error| {
668 PlottingError::LowLevel(format!("Failed to spawn blocking tokio task: {error}"))
669 })??;
670
671 sector_write_offset += sector_chunk_size;
672 }
673 drop(sector);
674
675 if total_received != sector_size {
676 return Ok(Err(PlottingError::LowLevel(format!(
677 "Received only {total_received} sector bytes out of {sector_size} \
678 expected bytes"
679 ))));
680 }
681 }
682 {
683 let encoded_sector_metadata = plotted_sector.sector_metadata.encode();
684 let write_fut = task::spawn_blocking({
685 let metadata_file = Arc::clone(metadata_file);
686
687 move || {
688 metadata_file.write_all_at(
689 &encoded_sector_metadata,
690 RESERVED_PLOT_METADATA
691 + (u64::from(sector_index) * encoded_sector_metadata.len() as u64),
692 )
693 }
694 });
695 write_fut.await.map_err(|error| {
696 PlottingError::LowLevel(format!("Failed to spawn blocking tokio task: {error}"))
697 })??;
698 }
699
700 let time = start.elapsed();
701 if let Some(metrics) = metrics {
702 metrics.sector_writing_time.observe(time.as_secs_f64());
703 metrics.sector_written.inc();
704 }
705 handlers.sector_update.call_simple(&(
706 sector_index,
707 SectorUpdate::Plotting(SectorPlottingDetails::Written(time)),
708 ));
709 }
710
711 Ok(Ok(plotted_sector))
712}
713
714pub(super) struct PlottingSchedulerOptions<NC> {
715 pub(super) public_key_hash: Blake3Hash,
716 pub(super) sectors_indices_left_to_plot: Range<SectorIndex>,
717 pub(super) target_sector_count: SectorIndex,
718 pub(super) last_archived_segment_index: SegmentIndex,
719 pub(super) min_sector_lifetime: HistorySize,
720 pub(super) node_client: NC,
721 pub(super) handlers: Arc<Handlers>,
722 pub(super) sectors_metadata: Arc<AsyncRwLock<Vec<SectorMetadataChecksummed>>>,
723 pub(super) sectors_to_plot_sender: mpsc::Sender<SectorToPlot>,
724 pub(super) new_segment_processing_delay: Duration,
727 pub(super) metrics: Option<Arc<SingleDiskFarmMetrics>>,
728}
729
730pub(super) async fn plotting_scheduler<NC>(
731 plotting_scheduler_options: PlottingSchedulerOptions<NC>,
732) -> Result<(), BackgroundTaskError>
733where
734 NC: NodeClient,
735{
736 let PlottingSchedulerOptions {
737 public_key_hash,
738 sectors_indices_left_to_plot,
739 target_sector_count,
740 last_archived_segment_index,
741 min_sector_lifetime,
742 node_client,
743 handlers,
744 sectors_metadata,
745 sectors_to_plot_sender,
746 new_segment_processing_delay,
747 metrics,
748 } = plotting_scheduler_options;
749
750 let last_archived_segment = node_client
754 .segment_headers(vec![last_archived_segment_index])
755 .await
756 .map_err(|error| PlottingError::FailedToGetSegmentHeader { error })?
757 .into_iter()
758 .next()
759 .flatten()
760 .ok_or(PlottingError::MissingArchivedSegmentHeader {
761 segment_index: last_archived_segment_index,
762 })?;
763
764 let (archived_segments_sender, archived_segments_receiver) =
765 watch::channel(last_archived_segment);
766
767 let read_archived_segments_notifications_fut = read_archived_segments_notifications(
768 &node_client,
769 archived_segments_sender,
770 new_segment_processing_delay,
771 );
772
773 let send_plotting_notifications_fut = send_plotting_notifications(
774 public_key_hash,
775 sectors_indices_left_to_plot,
776 target_sector_count,
777 min_sector_lifetime,
778 &node_client,
779 &handlers,
780 sectors_metadata,
781 archived_segments_receiver,
782 sectors_to_plot_sender,
783 &metrics,
784 );
785
786 select! {
787 result = read_archived_segments_notifications_fut.fuse() => {
788 result
789 }
790 result = send_plotting_notifications_fut.fuse() => {
791 result
792 }
793 }
794}
795
796async fn read_archived_segments_notifications<NC>(
797 node_client: &NC,
798 archived_segments_sender: watch::Sender<SegmentHeader>,
799 new_segment_processing_delay: Duration,
800) -> Result<(), BackgroundTaskError>
801where
802 NC: NodeClient,
803{
804 info!("Subscribing to archived segments");
805
806 let mut archived_segments_notifications = node_client
807 .subscribe_archived_segment_headers()
808 .await
809 .map_err(|error| PlottingError::FailedToSubscribeArchivedSegments { error })?;
810
811 while let Some(segment_header) = archived_segments_notifications.next().await {
812 debug!(?segment_header, "New archived segment");
813 if let Err(error) = node_client
814 .acknowledge_archived_segment_header(segment_header.segment_index())
815 .await
816 {
817 debug!(%error, "Failed to acknowledge segment header");
818 }
819
820 let delay = Duration::from_secs(thread_rng().gen_range(
823 new_segment_processing_delay.as_secs() / 10..=new_segment_processing_delay.as_secs(),
824 ));
825 tokio::time::sleep(delay).await;
826
827 if archived_segments_sender.send(segment_header).is_err() {
828 break;
829 }
830 }
831
832 Ok(())
833}
834
835struct SectorToReplot {
836 sector_index: SectorIndex,
837 expires_at: SegmentIndex,
838}
839
840#[allow(clippy::too_many_arguments)]
841async fn send_plotting_notifications<NC>(
842 public_key_hash: Blake3Hash,
843 sectors_indices_left_to_plot: Range<SectorIndex>,
844 target_sector_count: SectorIndex,
845 min_sector_lifetime: HistorySize,
846 node_client: &NC,
847 handlers: &Handlers,
848 sectors_metadata: Arc<AsyncRwLock<Vec<SectorMetadataChecksummed>>>,
849 mut archived_segments_receiver: watch::Receiver<SegmentHeader>,
850 mut sectors_to_plot_sender: mpsc::Sender<SectorToPlot>,
851 metrics: &Option<Arc<SingleDiskFarmMetrics>>,
852) -> Result<(), BackgroundTaskError>
853where
854 NC: NodeClient,
855{
856 for sector_index in sectors_indices_left_to_plot {
858 let (acknowledgement_sender, acknowledgement_receiver) = oneshot::channel();
859 if let Err(error) = sectors_to_plot_sender
860 .send(SectorToPlot {
861 sector_index,
862 progress: sector_index as f32 / target_sector_count as f32 * 100.0,
863 last_queued: sector_index + 1 == target_sector_count,
864 acknowledgement_sender,
865 })
866 .await
867 {
868 warn!(%error, "Failed to send sector index for initial plotting");
869 return Ok(());
870 }
871
872 let _ = acknowledgement_receiver.await;
874 }
875
876 let mut sectors_expire_at = vec![None::<SegmentIndex>; usize::from(target_sector_count)];
877 let mut sectors_to_replot = Vec::with_capacity(usize::from(target_sector_count) / 10);
879
880 loop {
881 let segment_index = archived_segments_receiver
882 .borrow_and_update()
883 .segment_index();
884 trace!(%segment_index, "New archived segment received");
885
886 let sectors_metadata = sectors_metadata.read().await;
887 let sectors_to_check = sectors_metadata
888 .iter()
889 .map(|sector_metadata| (sector_metadata.sector_index, sector_metadata.history_size));
890 for (sector_index, history_size) in sectors_to_check {
891 if let Some(Some(expires_at)) =
892 sectors_expire_at.get(usize::from(sector_index)).copied()
893 {
894 trace!(
895 %sector_index,
896 %history_size,
897 %expires_at,
898 "Checking sector for expiration"
899 );
900 if expires_at <= (segment_index + SegmentIndex::ONE) {
903 debug!(
904 %sector_index,
905 %history_size,
906 %expires_at,
907 "Sector expires soon #1, scheduling replotting"
908 );
909
910 let expiration_details = if expires_at <= segment_index {
911 if let Some(metrics) = metrics {
912 metrics.update_sector_state(SectorState::Expired);
913 }
914 SectorExpirationDetails::Expired
915 } else {
916 if let Some(metrics) = metrics {
917 metrics.update_sector_state(SectorState::AboutToExpire);
918 }
919 SectorExpirationDetails::AboutToExpire
920 };
921 handlers
922 .sector_update
923 .call_simple(&(sector_index, SectorUpdate::Expiration(expiration_details)));
924
925 sectors_to_replot.push(SectorToReplot {
927 sector_index,
928 expires_at,
929 });
930 }
931 continue;
932 }
933
934 if let Some(expiration_check_segment_index) = history_size
935 .sector_expiration_check(min_sector_lifetime)
936 .map(|expiration_check_history_size| expiration_check_history_size.segment_index())
937 {
938 trace!(
939 %sector_index,
940 %history_size,
941 %expiration_check_segment_index,
942 "Determined sector expiration check segment index"
943 );
944 let maybe_sector_expiration_check_segment_commitment = node_client
945 .segment_headers(vec![expiration_check_segment_index])
946 .await
947 .map_err(|error| PlottingError::FailedToGetSegmentHeader { error })?
948 .into_iter()
949 .next()
950 .flatten()
951 .map(|segment_header| segment_header.segment_commitment());
952
953 if let Some(sector_expiration_check_segment_commitment) =
954 maybe_sector_expiration_check_segment_commitment
955 {
956 let sector_id = SectorId::new(public_key_hash, sector_index, history_size);
957 let expiration_history_size = sector_id
958 .derive_expiration_history_size(
959 history_size,
960 §or_expiration_check_segment_commitment,
961 min_sector_lifetime,
962 )
963 .expect(
964 "Farmers internally stores correct history size in sector \
965 metadata; qed",
966 );
967
968 let expires_at = expiration_history_size.segment_index();
969
970 trace!(
971 %sector_index,
972 %history_size,
973 sector_expire_at = %expires_at,
974 "Determined sector expiration segment index"
975 );
976 if expires_at <= (segment_index + SegmentIndex::ONE) {
979 debug!(
980 %sector_index,
981 %history_size,
982 %expires_at,
983 "Sector expires soon #2, scheduling replotting"
984 );
985
986 let expiration_details = if expires_at <= segment_index {
987 if let Some(metrics) = metrics {
988 metrics.update_sector_state(SectorState::Expired);
989 }
990 SectorExpirationDetails::Expired
991 } else {
992 if let Some(metrics) = metrics {
993 metrics.update_sector_state(SectorState::AboutToExpire);
994 }
995 SectorExpirationDetails::AboutToExpire
996 };
997 handlers.sector_update.call_simple(&(
998 sector_index,
999 SectorUpdate::Expiration(expiration_details),
1000 ));
1001
1002 sectors_to_replot.push(SectorToReplot {
1004 sector_index,
1005 expires_at,
1006 });
1007 } else {
1008 trace!(
1009 %sector_index,
1010 %history_size,
1011 sector_expire_at = %expires_at,
1012 "Sector expires later, remembering sector expiration"
1013 );
1014
1015 handlers.sector_update.call_simple(&(
1016 sector_index,
1017 SectorUpdate::Expiration(SectorExpirationDetails::Determined {
1018 expires_at,
1019 }),
1020 ));
1021
1022 if let Some(expires_at_entry) =
1024 sectors_expire_at.get_mut(usize::from(sector_index))
1025 {
1026 expires_at_entry.replace(expires_at);
1027 }
1028 }
1029 }
1030 }
1031 }
1032 drop(sectors_metadata);
1033
1034 let sectors_queued = sectors_to_replot.len();
1035 sectors_to_replot.sort_by_key(|sector_to_replot| sector_to_replot.expires_at);
1036 for (index, SectorToReplot { sector_index, .. }) in sectors_to_replot.drain(..).enumerate()
1037 {
1038 let (acknowledgement_sender, acknowledgement_receiver) = oneshot::channel();
1039 if let Err(error) = sectors_to_plot_sender
1040 .send(SectorToPlot {
1041 sector_index,
1042 progress: index as f32 / sectors_queued as f32 * 100.0,
1043 last_queued: index + 1 == sectors_queued,
1044 acknowledgement_sender,
1045 })
1046 .await
1047 {
1048 warn!(%error, "Failed to send sector index for replotting");
1049 return Ok(());
1050 }
1051
1052 let _ = acknowledgement_receiver.await;
1054
1055 if let Some(expires_at_entry) = sectors_expire_at.get_mut(usize::from(sector_index)) {
1056 expires_at_entry.take();
1057 }
1058 }
1059
1060 if archived_segments_receiver.changed().await.is_err() {
1061 break;
1062 }
1063 }
1064
1065 Ok(())
1066}