1use super::{
2 manager::StaticFileProviderInner, metrics::StaticFileProviderMetrics, StaticFileProvider,
3};
4use crate::providers::static_file::metrics::StaticFileProviderOperation;
5use alloy_consensus::BlockHeader;
6use alloy_primitives::{BlockHash, BlockNumber, TxNumber, U256};
7use parking_lot::{lock_api::RwLockWriteGuard, RawRwLock, RwLock};
8use reth_codecs::Compact;
9use reth_db::models::{AccountBeforeTx, StorageBeforeTx};
10use reth_db_api::models::CompactU256;
11use reth_nippy_jar::{NippyJar, NippyJarError, NippyJarWriter};
12use reth_node_types::NodePrimitives;
13use reth_primitives_traits::FastInstant as Instant;
14use reth_static_file_types::{
15 ChangesetOffset, ChangesetOffsetReader, ChangesetOffsetWriter, SegmentHeader,
16 SegmentRangeInclusive, StaticFileSegment,
17};
18use reth_storage_errors::provider::{ProviderError, ProviderResult, StaticFileWriterError};
19use std::{
20 borrow::Borrow,
21 cmp::Ordering,
22 fmt::Debug,
23 path::{Path, PathBuf},
24 sync::{Arc, Weak},
25};
26use tracing::{debug, instrument};
27
28#[derive(Debug, Clone, Copy)]
30enum PruneStrategy {
31 Headers {
33 num_blocks: u64,
35 },
36 Transactions {
38 num_rows: u64,
40 last_block: BlockNumber,
42 },
43 Receipts {
45 num_rows: u64,
47 last_block: BlockNumber,
49 },
50 TransactionSenders {
52 num_rows: u64,
54 last_block: BlockNumber,
56 },
57 AccountChangeSets {
59 last_block: BlockNumber,
61 },
62 StorageChangeSets {
64 last_block: BlockNumber,
66 },
67}
68
69#[derive(Debug)]
74pub(crate) struct StaticFileWriters<N> {
75 headers: RwLock<Option<StaticFileProviderRW<N>>>,
76 transactions: RwLock<Option<StaticFileProviderRW<N>>>,
77 receipts: RwLock<Option<StaticFileProviderRW<N>>>,
78 transaction_senders: RwLock<Option<StaticFileProviderRW<N>>>,
79 account_change_sets: RwLock<Option<StaticFileProviderRW<N>>>,
80 storage_change_sets: RwLock<Option<StaticFileProviderRW<N>>>,
81}
82
83impl<N> Default for StaticFileWriters<N> {
84 fn default() -> Self {
85 Self {
86 headers: Default::default(),
87 transactions: Default::default(),
88 receipts: Default::default(),
89 transaction_senders: Default::default(),
90 account_change_sets: Default::default(),
91 storage_change_sets: Default::default(),
92 }
93 }
94}
95
96impl<N: NodePrimitives> StaticFileWriters<N> {
97 pub(crate) fn get_or_create(
98 &self,
99 segment: StaticFileSegment,
100 create_fn: impl FnOnce() -> ProviderResult<StaticFileProviderRW<N>>,
101 ) -> ProviderResult<StaticFileProviderRWRefMut<'_, N>> {
102 let mut write_guard = match segment {
103 StaticFileSegment::Headers => self.headers.write(),
104 StaticFileSegment::Transactions => self.transactions.write(),
105 StaticFileSegment::Receipts => self.receipts.write(),
106 StaticFileSegment::TransactionSenders => self.transaction_senders.write(),
107 StaticFileSegment::AccountChangeSets => self.account_change_sets.write(),
108 StaticFileSegment::StorageChangeSets => self.storage_change_sets.write(),
109 };
110
111 if write_guard.is_none() {
112 *write_guard = Some(create_fn()?);
113 }
114
115 Ok(StaticFileProviderRWRefMut(write_guard))
116 }
117
118 pub(crate) fn remove(&self, segment: StaticFileSegment) {
120 let mut write_guard = match segment {
121 StaticFileSegment::Headers => self.headers.write(),
122 StaticFileSegment::Transactions => self.transactions.write(),
123 StaticFileSegment::Receipts => self.receipts.write(),
124 StaticFileSegment::TransactionSenders => self.transaction_senders.write(),
125 StaticFileSegment::AccountChangeSets => self.account_change_sets.write(),
126 StaticFileSegment::StorageChangeSets => self.storage_change_sets.write(),
127 };
128
129 *write_guard = None;
130 }
131
132 #[instrument(
133 name = "StaticFileWriters::commit",
134 level = "debug",
135 target = "providers::static_file",
136 skip_all
137 )]
138 pub(crate) fn commit(&self) -> ProviderResult<()> {
139 debug!(target: "providers::static_file", "Committing all static file segments");
140
141 for writer_lock in [
142 &self.headers,
143 &self.transactions,
144 &self.receipts,
145 &self.transaction_senders,
146 &self.account_change_sets,
147 &self.storage_change_sets,
148 ] {
149 let mut writer = writer_lock.write();
150 if let Some(writer) = writer.as_mut() {
151 writer.commit()?;
152 }
153 }
154
155 debug!(target: "providers::static_file", "Committed all static file segments");
156 Ok(())
157 }
158
159 pub(crate) fn has_unwind_queued(&self) -> bool {
160 for writer_lock in [
161 &self.headers,
162 &self.transactions,
163 &self.receipts,
164 &self.transaction_senders,
165 &self.account_change_sets,
166 &self.storage_change_sets,
167 ] {
168 let writer = writer_lock.read();
169 if let Some(writer) = writer.as_ref() &&
170 writer.will_prune_on_commit()
171 {
172 return true
173 }
174 }
175 false
176 }
177
178 #[instrument(
183 name = "StaticFileWriters::finalize",
184 level = "debug",
185 target = "providers::static_file",
186 skip_all
187 )]
188 pub(crate) fn finalize(&self) -> ProviderResult<()> {
189 debug!(target: "providers::static_file", "Finalizing all static file segments into disk");
190
191 for writer_lock in [
192 &self.headers,
193 &self.transactions,
194 &self.receipts,
195 &self.transaction_senders,
196 &self.account_change_sets,
197 &self.storage_change_sets,
198 ] {
199 let mut writer = writer_lock.write();
200 if let Some(writer) = writer.as_mut() {
201 writer.finalize()?;
202 }
203 }
204
205 debug!(target: "providers::static_file", "Finalized all static file segments into disk");
206 Ok(())
207 }
208}
209
210#[derive(Debug)]
212pub struct StaticFileProviderRWRefMut<'a, N>(
213 pub(crate) RwLockWriteGuard<'a, RawRwLock, Option<StaticFileProviderRW<N>>>,
214);
215
216impl<N> std::ops::DerefMut for StaticFileProviderRWRefMut<'_, N> {
217 fn deref_mut(&mut self) -> &mut Self::Target {
218 self.0.as_mut().expect("static file writer provider should be init")
220 }
221}
222
223impl<N> std::ops::Deref for StaticFileProviderRWRefMut<'_, N> {
224 type Target = StaticFileProviderRW<N>;
225
226 fn deref(&self) -> &Self::Target {
227 self.0.as_ref().expect("static file writer provider should be init")
229 }
230}
231
232#[derive(Debug)]
233pub struct StaticFileProviderRW<N> {
235 reader: Weak<StaticFileProviderInner<N>>,
240 writer: NippyJarWriter<SegmentHeader>,
242 data_path: PathBuf,
244 buf: Vec<u8>,
246 metrics: Option<Arc<StaticFileProviderMetrics>>,
248 prune_on_commit: Option<PruneStrategy>,
250 synced: bool,
252 changeset_offsets: Option<ChangesetOffsetWriter>,
254 current_changeset_offset: Option<ChangesetOffset>,
256}
257
258impl<N: NodePrimitives> StaticFileProviderRW<N> {
259 pub fn new(
264 segment: StaticFileSegment,
265 block: BlockNumber,
266 reader: Weak<StaticFileProviderInner<N>>,
267 metrics: Option<Arc<StaticFileProviderMetrics>>,
268 ) -> ProviderResult<Self> {
269 let (writer, data_path) = Self::open(segment, block, reader.clone(), metrics.clone())?;
270
271 let mut writer = Self {
273 writer,
274 data_path,
275 buf: Vec::with_capacity(100),
276 reader,
277 metrics,
278 prune_on_commit: None,
279 synced: false,
280 changeset_offsets: None,
281 current_changeset_offset: None,
282 };
283
284 writer.ensure_end_range_consistency()?;
287
288 if segment.is_change_based() {
290 writer.heal_changeset_sidecar()?;
291 }
292
293 Ok(writer)
294 }
295
296 fn open(
297 segment: StaticFileSegment,
298 block: u64,
299 reader: Weak<StaticFileProviderInner<N>>,
300 metrics: Option<Arc<StaticFileProviderMetrics>>,
301 ) -> ProviderResult<(NippyJarWriter<SegmentHeader>, PathBuf)> {
302 let start = Instant::now();
303
304 let static_file_provider = Self::upgrade_provider_to_strong_reference(&reader);
305
306 let block_range = static_file_provider.find_fixed_range(segment, block);
307 let (jar, path) = match static_file_provider.get_segment_provider_for_block(
308 segment,
309 block_range.start(),
310 None,
311 ) {
312 Ok(provider) => (
313 NippyJar::load(provider.data_path()).map_err(ProviderError::other)?,
314 provider.data_path().into(),
315 ),
316 Err(ProviderError::MissingStaticFileBlock(_, _)) => {
317 let path = static_file_provider.directory().join(segment.filename(&block_range));
318 (create_jar(segment, &path, block_range), path)
319 }
320 Err(err) => return Err(err),
321 };
322
323 let result = match NippyJarWriter::new(jar) {
324 Ok(writer) => Ok((writer, path)),
325 Err(NippyJarError::FrozenJar) => {
326 Err(ProviderError::FinalizedStaticFile(segment, block))
328 }
329 Err(e) => Err(ProviderError::other(e)),
330 }?;
331
332 if let Some(metrics) = &metrics {
333 metrics.record_segment_operation(
334 segment,
335 StaticFileProviderOperation::OpenWriter,
336 Some(start.elapsed()),
337 );
338 }
339
340 Ok(result)
341 }
342
343 fn ensure_end_range_consistency(&mut self) -> ProviderResult<()> {
352 let expected_rows = if self.user_header().segment().is_headers() {
354 self.user_header().block_len().unwrap_or_default()
355 } else {
356 self.user_header().tx_len().unwrap_or_default()
357 };
358 let actual_rows = self.writer.rows() as u64;
359 let pruned_rows = expected_rows.saturating_sub(actual_rows);
360 if pruned_rows > 0 {
361 self.user_header_mut().prune(pruned_rows);
362 }
363
364 debug!(
365 target: "providers::static_file",
366 segment = ?self.writer.user_header().segment(),
367 path = ?self.data_path,
368 pruned_rows,
369 "Ensuring end range consistency"
370 );
371
372 self.writer.commit().map_err(ProviderError::other)?;
373
374 self.update_index()?;
376 Ok(())
377 }
378
379 pub const fn will_prune_on_commit(&self) -> bool {
381 self.prune_on_commit.is_some()
382 }
383
384 fn heal_changeset_sidecar(&mut self) -> ProviderResult<()> {
392 let csoff_path = self.data_path.with_extension("csoff");
393
394 let header_claims_blocks = self.writer.user_header().changeset_offsets_len();
396 let actual_nippy_rows = self.writer.rows() as u64;
397
398 let actual_sidecar_blocks = if csoff_path.exists() {
400 let file_len = reth_fs_util::metadata(&csoff_path).map_err(ProviderError::other)?.len();
401 let aligned_len = file_len - (file_len % 16);
403 aligned_len / 16
404 } else {
405 0
406 };
407
408 if header_claims_blocks == 0 && actual_sidecar_blocks == 0 {
410 self.changeset_offsets =
411 Some(ChangesetOffsetWriter::new(&csoff_path, 0).map_err(ProviderError::other)?);
412 return Ok(());
413 }
414
415 let valid_blocks = if actual_sidecar_blocks > 0 {
417 let reader = ChangesetOffsetReader::new(&csoff_path, actual_sidecar_blocks)
418 .map_err(ProviderError::other)?;
419
420 let mut valid = 0u64;
423 for i in 0..actual_sidecar_blocks {
424 if let Some(offset) = reader.get(i).map_err(ProviderError::other)? {
425 if offset.offset() + offset.num_changes() <= actual_nippy_rows {
426 valid = i + 1;
427 } else {
428 break;
430 }
431 }
432 }
433 valid
434 } else {
435 0
436 };
437
438 let correct_blocks = valid_blocks.min(header_claims_blocks);
441
442 let mut needs_header_commit = false;
444
445 if correct_blocks != header_claims_blocks || actual_sidecar_blocks != correct_blocks {
446 tracing::warn!(
447 target: "reth::static_file",
448 path = %csoff_path.display(),
449 header_claims = header_claims_blocks,
450 sidecar_has = actual_sidecar_blocks,
451 valid_blocks = correct_blocks,
452 actual_rows = actual_nippy_rows,
453 "Three-way healing: syncing header, sidecar, and NippyJar state"
454 );
455
456 if actual_sidecar_blocks > correct_blocks {
458 use std::fs::OpenOptions;
459 let file = OpenOptions::new()
460 .write(true)
461 .open(&csoff_path)
462 .map_err(ProviderError::other)?;
463 file.set_len(correct_blocks * 16).map_err(ProviderError::other)?;
464 file.sync_all().map_err(ProviderError::other)?;
465
466 tracing::debug!(
467 target: "reth::static_file",
468 "Truncated sidecar from {} to {} blocks",
469 actual_sidecar_blocks,
470 correct_blocks
471 );
472 }
473
474 if correct_blocks < header_claims_blocks {
476 let blocks_removed = header_claims_blocks - correct_blocks;
479 self.writer.user_header_mut().prune(blocks_removed);
480
481 tracing::debug!(
482 target: "reth::static_file",
483 "Updated header: removed {} blocks (changeset_offsets_len: {} -> {})",
484 blocks_removed,
485 header_claims_blocks,
486 correct_blocks
487 );
488
489 needs_header_commit = true;
490 }
491 } else {
492 tracing::debug!(
493 target: "reth::static_file",
494 path = %csoff_path.display(),
495 blocks = correct_blocks,
496 "Changeset sidecar consistent, no healing needed"
497 );
498 }
499
500 let csoff_writer = ChangesetOffsetWriter::new(&csoff_path, correct_blocks)
502 .map_err(ProviderError::other)?;
503
504 self.changeset_offsets = Some(csoff_writer);
505
506 if needs_header_commit {
508 self.writer.commit().map_err(ProviderError::other)?;
509
510 tracing::info!(
511 target: "reth::static_file",
512 path = %csoff_path.display(),
513 blocks = correct_blocks,
514 "Committed healed changeset offset header"
515 );
516 }
517
518 Ok(())
519 }
520
521 fn flush_current_changeset_offset(&mut self) -> ProviderResult<()> {
529 if !self.writer.user_header().segment().is_change_based() {
530 return Ok(());
531 }
532
533 if let Some(offset) = self.current_changeset_offset.take() &&
534 let Some(writer) = &mut self.changeset_offsets
535 {
536 writer.append(&offset).map_err(ProviderError::other)?;
537 }
538 Ok(())
539 }
540
541 pub fn sync_all(&mut self) -> ProviderResult<()> {
548 if self.prune_on_commit.is_some() {
549 return Err(StaticFileWriterError::FinalizeWithPruneQueued.into());
550 }
551
552 self.flush_current_changeset_offset()?;
554 if let Some(writer) = &mut self.changeset_offsets {
555 writer.sync().map_err(ProviderError::other)?;
556 self.writer.user_header_mut().set_changeset_offsets_len(writer.len());
558 }
559
560 if self.writer.is_dirty() {
561 self.writer.sync_all().map_err(ProviderError::other)?;
562 }
563 self.synced = true;
564 Ok(())
565 }
566
567 #[instrument(
573 name = "StaticFileProviderRW::finalize",
574 level = "debug",
575 target = "providers::static_file",
576 skip_all
577 )]
578 pub fn finalize(&mut self) -> ProviderResult<()> {
579 if self.prune_on_commit.is_some() {
580 return Err(StaticFileWriterError::FinalizeWithPruneQueued.into());
581 }
582 if self.writer.is_dirty() {
583 if !self.synced {
584 self.sync_all()?;
587 }
588
589 self.writer.finalize().map_err(ProviderError::other)?;
590 self.update_index()?;
591 }
592 self.synced = false;
593 Ok(())
594 }
595
596 #[instrument(
598 name = "StaticFileProviderRW::commit",
599 level = "debug",
600 target = "providers::static_file",
601 skip_all
602 )]
603 pub fn commit(&mut self) -> ProviderResult<()> {
604 let start = Instant::now();
605
606 if let Some(strategy) = self.prune_on_commit.take() {
608 debug!(
609 target: "providers::static_file",
610 segment = ?self.writer.user_header().segment(),
611 "Pruning data on commit"
612 );
613 match strategy {
614 PruneStrategy::Headers { num_blocks } => self.prune_header_data(num_blocks)?,
615 PruneStrategy::Transactions { num_rows, last_block } => {
616 self.prune_transaction_data(num_rows, last_block)?
617 }
618 PruneStrategy::Receipts { num_rows, last_block } => {
619 self.prune_receipt_data(num_rows, last_block)?
620 }
621 PruneStrategy::TransactionSenders { num_rows, last_block } => {
622 self.prune_transaction_sender_data(num_rows, last_block)?
623 }
624 PruneStrategy::AccountChangeSets { last_block } => {
625 self.prune_account_changeset_data(last_block)?
626 }
627 PruneStrategy::StorageChangeSets { last_block } => {
628 self.prune_storage_changeset_data(last_block)?
629 }
630 }
631 }
632
633 self.flush_current_changeset_offset()?;
636 if let Some(writer) = &mut self.changeset_offsets {
637 writer.sync().map_err(ProviderError::other)?;
638 self.writer.user_header_mut().set_changeset_offsets_len(writer.len());
640 }
641
642 if self.writer.is_dirty() {
643 debug!(
644 target: "providers::static_file",
645 segment = ?self.writer.user_header().segment(),
646 "Committing writer to disk"
647 );
648
649 self.writer.commit().map_err(ProviderError::other)?;
651
652 if let Some(metrics) = &self.metrics {
653 metrics.record_segment_operation(
654 self.writer.user_header().segment(),
655 StaticFileProviderOperation::CommitWriter,
656 Some(start.elapsed()),
657 );
658 }
659
660 debug!(
661 target: "providers::static_file",
662 segment = ?self.writer.user_header().segment(),
663 path = ?self.data_path,
664 duration = ?start.elapsed(),
665 "Committed writer to disk"
666 );
667
668 self.update_index()?;
669 }
670
671 Ok(())
672 }
673
674 #[cfg(feature = "test-utils")]
678 pub fn commit_without_sync_all(&mut self) -> ProviderResult<()> {
679 let start = Instant::now();
680
681 debug!(
682 target: "providers::static_file",
683 segment = ?self.writer.user_header().segment(),
684 "Committing writer to disk (without sync)"
685 );
686
687 self.writer.commit_without_sync_all().map_err(ProviderError::other)?;
689
690 if let Some(metrics) = &self.metrics {
691 metrics.record_segment_operation(
692 self.writer.user_header().segment(),
693 StaticFileProviderOperation::CommitWriter,
694 Some(start.elapsed()),
695 );
696 }
697
698 debug!(
699 target: "providers::static_file",
700 segment = ?self.writer.user_header().segment(),
701 path = ?self.data_path,
702 duration = ?start.elapsed(),
703 "Committed writer to disk (without sync)"
704 );
705
706 self.update_index()?;
707
708 Ok(())
709 }
710
711 fn update_index(&self) -> ProviderResult<()> {
713 let segment = self.writer.user_header().segment();
714
715 let segment_max_block = self
723 .writer
724 .user_header()
725 .block_range()
726 .as_ref()
727 .map(|block_range| block_range.end())
728 .or_else(|| {
729 let expected_start = self.writer.user_header().expected_block_start();
730 if expected_start <= self.reader().genesis_block_number() {
731 return None;
732 }
733
734 let prev_block = expected_start - 1;
735 let prev_range = self.reader().find_fixed_range(segment, prev_block);
736 let prev_path = self.reader().directory().join(segment.filename(&prev_range));
737 prev_path.exists().then_some(prev_block)
738 });
739
740 self.reader().update_index(segment, segment_max_block)
741 }
742
743 pub fn ensure_at_block(&mut self, advance_to: BlockNumber) -> ProviderResult<()> {
748 let current_block = if let Some(current_block_number) = self.current_block_number() {
749 current_block_number
750 } else {
751 let first_block = self.writer.user_header().expected_block_start();
755 self.increment_block(first_block)?;
756 first_block
757 };
758
759 match current_block.cmp(&advance_to) {
760 Ordering::Less => {
761 for block in current_block + 1..=advance_to {
762 self.increment_block(block)?;
763 }
764 }
765 Ordering::Equal => {}
766 Ordering::Greater => {
767 return Err(ProviderError::UnexpectedStaticFileBlockNumber(
768 self.writer.user_header().segment(),
769 current_block,
770 advance_to,
771 ));
772 }
773 }
774
775 Ok(())
776 }
777
778 pub fn initialize_pruned_anchor(&mut self, block: BlockNumber) -> ProviderResult<()> {
782 let header = self.user_header();
783 if header.segment().is_headers() ||
784 header.block_range().is_some() ||
785 self.writer.rows() != 0
786 {
787 return Err(ProviderError::other(StaticFileWriterError::new(
788 "pruned anchor requires a fresh non-header segment",
789 )))
790 }
791 if !header.expected_block_range().contains(block) {
792 return Err(ProviderError::other(StaticFileWriterError::new(
793 "pruned anchor is outside the static file range",
794 )))
795 }
796 self.writer.user_header_mut().set_block_range(block, block);
797 if self.user_header().segment().is_change_based() {
798 self.writer.user_header_mut().set_changeset_offsets_len(1);
799 self.current_changeset_offset = Some(ChangesetOffset::new(0, 0));
800 }
801 Ok(())
802 }
803
804 pub fn increment_block(&mut self, expected_block_number: BlockNumber) -> ProviderResult<()> {
807 let segment = self.writer.user_header().segment();
808
809 self.check_next_block_number(expected_block_number)?;
810
811 let start = Instant::now();
812 if let Some(last_block) = self.writer.user_header().block_end() {
813 if last_block == self.writer.user_header().expected_block_end() {
815 self.commit()?;
817
818 let (writer, data_path) =
820 Self::open(segment, last_block + 1, self.reader.clone(), self.metrics.clone())?;
821 self.writer = writer;
822 self.data_path = data_path.clone();
823
824 if segment.is_change_based() {
826 let csoff_path = data_path.with_extension("csoff");
827 self.changeset_offsets = Some(
828 ChangesetOffsetWriter::new(&csoff_path, 0).map_err(ProviderError::other)?,
829 );
830 }
831
832 *self.writer.user_header_mut() = SegmentHeader::new(
833 self.reader().find_fixed_range(segment, last_block + 1),
834 None,
835 None,
836 segment,
837 );
838 }
839 }
840
841 self.writer.user_header_mut().increment_block();
842
843 if segment.is_change_based() {
845 if let Some(offset) = self.current_changeset_offset.take() &&
847 let Some(writer) = &mut self.changeset_offsets
848 {
849 writer.append(&offset).map_err(ProviderError::other)?;
850 }
851 let new_offset = self.writer.rows() as u64;
853 self.current_changeset_offset = Some(ChangesetOffset::new(new_offset, 0));
854 }
855
856 if let Some(metrics) = &self.metrics {
857 metrics.record_segment_operation(
858 segment,
859 StaticFileProviderOperation::IncrementBlock,
860 Some(start.elapsed()),
861 );
862 }
863
864 Ok(())
865 }
866
867 pub fn current_block_number(&self) -> Option<u64> {
869 self.writer.user_header().block_end()
870 }
871
872 pub fn next_block_number(&self) -> u64 {
874 self.writer
878 .user_header()
879 .block_end()
880 .map(|b| b + 1)
881 .unwrap_or_else(|| self.writer.user_header().expected_block_start())
882 }
883
884 fn check_next_block_number(&self, expected_block_number: u64) -> ProviderResult<()> {
887 let next_static_file_block = self.next_block_number();
888
889 if expected_block_number != next_static_file_block {
890 return Err(ProviderError::UnexpectedStaticFileBlockNumber(
891 self.writer.user_header().segment(),
892 expected_block_number,
893 next_static_file_block,
894 ))
895 }
896 Ok(())
897 }
898
899 fn truncate_changesets(&mut self, last_block: u64) -> ProviderResult<()> {
905 let segment = self.writer.user_header().segment();
906 debug_assert!(segment.is_change_based());
907
908 let current_block_end = self
910 .writer
911 .user_header()
912 .block_end()
913 .ok_or(ProviderError::MissingStaticFileBlock(segment, 0))?;
914
915 if current_block_end <= last_block {
917 return Ok(())
918 }
919
920 self.ensure_above_pruned_anchor(last_block)?;
921
922 let mut expected_block_start = self.writer.user_header().expected_block_start();
924 while last_block < expected_block_start && expected_block_start > 0 {
925 self.delete_current_and_open_previous()?;
926 expected_block_start = self.writer.user_header().expected_block_start();
927 }
928
929 let block_start = self.writer.user_header().block_start().unwrap_or(expected_block_start);
931
932 let blocks_to_keep =
934 if last_block >= block_start { last_block - block_start + 1 } else { 0 };
935
936 let csoff_path = self.data_path.with_extension("csoff");
938 let changeset_offsets_len = self.writer.user_header().changeset_offsets_len();
939
940 self.flush_current_changeset_offset()?;
942
943 let rows_to_keep = if blocks_to_keep == 0 {
944 0
945 } else if blocks_to_keep >= changeset_offsets_len {
946 self.writer.rows() as u64
948 } else {
949 let reader = ChangesetOffsetReader::new(&csoff_path, changeset_offsets_len)
953 .map_err(ProviderError::other)?;
954 if let Some(next_offset) = reader.get(blocks_to_keep).map_err(ProviderError::other)? {
955 next_offset.offset()
956 } else {
957 self.writer.rows() as u64
959 }
960 };
961
962 let total_rows = self.writer.rows() as u64;
963 let rows_to_delete = total_rows.saturating_sub(rows_to_keep);
964
965 if rows_to_delete > 0 {
966 let current_block_end = self
968 .writer
969 .user_header()
970 .block_end()
971 .ok_or(ProviderError::MissingStaticFileBlock(segment, 0))?;
972 let blocks_to_remove = current_block_end - last_block;
973
974 self.writer.user_header_mut().prune(blocks_to_remove);
976
977 self.writer.prune_rows(rows_to_delete as usize).map_err(ProviderError::other)?;
979 }
980
981 self.writer.user_header_mut().set_block_range(block_start, last_block);
983
984 self.writer.user_header_mut().sync_changeset_offsets();
986
987 if let Some(writer) = &mut self.changeset_offsets {
989 writer.truncate(blocks_to_keep).map_err(ProviderError::other)?;
990 }
991
992 self.current_changeset_offset = None;
994
995 self.commit()?;
997
998 Ok(())
999 }
1000
1001 fn truncate(&mut self, num_rows: u64, last_block: Option<u64>) -> ProviderResult<()> {
1009 if let Some(last_block) = last_block {
1010 self.ensure_above_pruned_anchor(last_block)?;
1011 }
1012 let mut remaining_rows = num_rows;
1013 let segment = self.writer.user_header().segment();
1014 while remaining_rows > 0 {
1015 let len = if segment.is_block_based() {
1016 self.writer.user_header().block_len().unwrap_or_default()
1017 } else {
1018 self.writer.user_header().tx_len().unwrap_or_default()
1019 };
1020
1021 if remaining_rows >= len {
1022 let block_start = self.writer.user_header().expected_block_start();
1025
1026 if block_start != 0 &&
1032 (segment.is_headers() || last_block.is_some_and(|b| b < block_start))
1033 {
1034 self.delete_current_and_open_previous()?;
1035 } else {
1036 self.writer.user_header_mut().prune(len);
1038 self.writer.prune_rows(len as usize).map_err(ProviderError::other)?;
1039 break
1040 }
1041
1042 remaining_rows -= len;
1043 } else {
1044 self.writer.user_header_mut().prune(remaining_rows);
1046
1047 self.writer.prune_rows(remaining_rows as usize).map_err(ProviderError::other)?;
1049 remaining_rows = 0;
1050 }
1051 }
1052
1053 if let Some(last_block) = last_block {
1055 let mut expected_block_start = self.writer.user_header().expected_block_start();
1056
1057 if num_rows == 0 {
1058 while last_block < expected_block_start {
1062 self.delete_current_and_open_previous()?;
1063 expected_block_start = self.writer.user_header().expected_block_start();
1064 }
1065 }
1066 let block_start =
1068 self.writer.user_header().block_start().unwrap_or(expected_block_start);
1069 self.writer.user_header_mut().set_block_range(block_start, last_block);
1070 }
1071
1072 self.commit()?;
1074
1075 Ok(())
1076 }
1077
1078 fn delete_current_and_open_previous(&mut self) -> Result<(), ProviderError> {
1081 let segment = self.user_header().segment();
1082 let current_path = self.data_path.clone();
1083 let (previous_writer, data_path) = Self::open(
1084 segment,
1085 self.writer.user_header().expected_block_start() - 1,
1086 self.reader.clone(),
1087 self.metrics.clone(),
1088 )?;
1089 self.writer = previous_writer;
1090 self.writer.set_dirty();
1091 self.data_path = data_path.clone();
1092
1093 if segment.is_change_based() {
1095 let csoff_path = current_path.with_extension("csoff");
1096 if csoff_path.exists() {
1097 std::fs::remove_file(&csoff_path).map_err(ProviderError::other)?;
1098 }
1099 let new_csoff_path = data_path.with_extension("csoff");
1101 let committed_len = self.writer.user_header().changeset_offsets_len();
1102 self.changeset_offsets = Some(
1103 ChangesetOffsetWriter::new(&new_csoff_path, committed_len)
1104 .map_err(ProviderError::other)?,
1105 );
1106 }
1107
1108 self.current_changeset_offset = None;
1110
1111 NippyJar::<SegmentHeader>::load(¤t_path)
1112 .map_err(ProviderError::other)?
1113 .delete()
1114 .map_err(ProviderError::other)?;
1115 Ok(())
1116 }
1117
1118 fn ensure_above_pruned_anchor(&self, last_block: BlockNumber) -> ProviderResult<()> {
1121 let header = self.writer.user_header();
1122 let lowest = self.reader().get_lowest_range_start(header.segment());
1123 let current = header
1126 .block_start()
1127 .filter(|start| lowest.is_none() || *start > header.expected_block_start());
1128 if current.into_iter().chain(lowest).any(|start| last_block < start) {
1129 return Err(ProviderError::other(StaticFileWriterError::new(
1130 "cannot unwind below the pruned anchor",
1131 )))
1132 }
1133 Ok(())
1134 }
1135
1136 fn append_column<T: Compact>(&mut self, column: T) -> ProviderResult<()> {
1138 self.buf.clear();
1139 column.to_compact(&mut self.buf);
1140
1141 self.writer.append_column(Some(Ok(&self.buf))).map_err(ProviderError::other)?;
1142 Ok(())
1143 }
1144
1145 fn append_with_tx_number<V: Compact>(
1147 &mut self,
1148 tx_num: TxNumber,
1149 value: V,
1150 ) -> ProviderResult<()> {
1151 if let Some(range) = self.writer.user_header().tx_range() {
1152 let next_tx = range.end() + 1;
1153 if next_tx != tx_num {
1154 return Err(ProviderError::UnexpectedStaticFileTxNumber(
1155 self.writer.user_header().segment(),
1156 tx_num,
1157 next_tx,
1158 ))
1159 }
1160 self.writer.user_header_mut().increment_tx();
1161 } else {
1162 self.writer.user_header_mut().set_tx_range(tx_num, tx_num);
1163 }
1164
1165 self.append_column(value)?;
1166
1167 Ok(())
1168 }
1169
1170 fn append_change<V: Compact>(&mut self, change: &V) -> ProviderResult<()> {
1172 if let Some(ref mut offset) = self.current_changeset_offset {
1173 offset.increment_num_changes();
1174 }
1175 self.append_column(change)?;
1176 Ok(())
1177 }
1178
1179 pub fn append_header(&mut self, header: &N::BlockHeader, hash: &BlockHash) -> ProviderResult<()>
1184 where
1185 N::BlockHeader: Compact,
1186 {
1187 self.append_header_with_td(header, U256::ZERO, hash)
1188 }
1189
1190 pub fn append_header_with_td(
1195 &mut self,
1196 header: &N::BlockHeader,
1197 total_difficulty: U256,
1198 hash: &BlockHash,
1199 ) -> ProviderResult<()>
1200 where
1201 N::BlockHeader: Compact,
1202 {
1203 let start = Instant::now();
1204 self.ensure_no_queued_prune()?;
1205
1206 debug_assert!(self.writer.user_header().segment() == StaticFileSegment::Headers);
1207
1208 self.increment_block(header.number())?;
1209
1210 self.append_column(header)?;
1211 self.append_column(CompactU256::from(total_difficulty))?;
1212 self.append_column(hash)?;
1213
1214 if let Some(metrics) = &self.metrics {
1215 metrics.record_segment_operation(
1216 StaticFileSegment::Headers,
1217 StaticFileProviderOperation::Append,
1218 Some(start.elapsed()),
1219 );
1220 }
1221
1222 Ok(())
1223 }
1224
1225 pub fn append_header_direct(
1228 &mut self,
1229 header: &N::BlockHeader,
1230 total_difficulty: U256,
1231 hash: &BlockHash,
1232 ) -> ProviderResult<()>
1233 where
1234 N::BlockHeader: Compact,
1235 {
1236 let start = Instant::now();
1237 self.ensure_no_queued_prune()?;
1238
1239 debug_assert!(self.writer.user_header().segment() == StaticFileSegment::Headers);
1240
1241 self.append_column(header)?;
1242 self.append_column(CompactU256::from(total_difficulty))?;
1243 self.append_column(hash)?;
1244
1245 if let Some(metrics) = &self.metrics {
1246 metrics.record_segment_operation(
1247 StaticFileSegment::Headers,
1248 StaticFileProviderOperation::Append,
1249 Some(start.elapsed()),
1250 );
1251 }
1252
1253 Ok(())
1254 }
1255
1256 pub fn append_transaction(&mut self, tx_num: TxNumber, tx: &N::SignedTx) -> ProviderResult<()>
1261 where
1262 N::SignedTx: Compact,
1263 {
1264 let start = Instant::now();
1265 self.ensure_no_queued_prune()?;
1266
1267 debug_assert!(self.writer.user_header().segment() == StaticFileSegment::Transactions);
1268 self.append_with_tx_number(tx_num, tx)?;
1269
1270 if let Some(metrics) = &self.metrics {
1271 metrics.record_segment_operation(
1272 StaticFileSegment::Transactions,
1273 StaticFileProviderOperation::Append,
1274 Some(start.elapsed()),
1275 );
1276 }
1277
1278 Ok(())
1279 }
1280
1281 pub fn append_receipt(&mut self, tx_num: TxNumber, receipt: &N::Receipt) -> ProviderResult<()>
1286 where
1287 N::Receipt: Compact,
1288 {
1289 let start = Instant::now();
1290 self.ensure_no_queued_prune()?;
1291
1292 debug_assert!(self.writer.user_header().segment() == StaticFileSegment::Receipts);
1293 self.append_with_tx_number(tx_num, receipt)?;
1294
1295 if let Some(metrics) = &self.metrics {
1296 metrics.record_segment_operation(
1297 StaticFileSegment::Receipts,
1298 StaticFileProviderOperation::Append,
1299 Some(start.elapsed()),
1300 );
1301 }
1302
1303 Ok(())
1304 }
1305
1306 pub fn append_receipts<I, R>(&mut self, receipts: I) -> ProviderResult<()>
1308 where
1309 I: Iterator<Item = Result<(TxNumber, R), ProviderError>>,
1310 R: Borrow<N::Receipt>,
1311 N::Receipt: Compact,
1312 {
1313 debug_assert!(self.writer.user_header().segment() == StaticFileSegment::Receipts);
1314
1315 let mut receipts_iter = receipts.into_iter().peekable();
1316 if receipts_iter.peek().is_none() {
1318 return Ok(());
1319 }
1320
1321 let start = Instant::now();
1322 self.ensure_no_queued_prune()?;
1323
1324 let mut count: u64 = 0;
1326
1327 for receipt_result in receipts_iter {
1328 let (tx_num, receipt) = receipt_result?;
1329 self.append_with_tx_number(tx_num, receipt.borrow())?;
1330 count += 1;
1331 }
1332
1333 if let Some(metrics) = &self.metrics {
1334 metrics.record_segment_operations(
1335 StaticFileSegment::Receipts,
1336 StaticFileProviderOperation::Append,
1337 count,
1338 Some(start.elapsed()),
1339 );
1340 }
1341
1342 Ok(())
1343 }
1344
1345 pub fn append_transaction_sender(
1350 &mut self,
1351 tx_num: TxNumber,
1352 sender: &alloy_primitives::Address,
1353 ) -> ProviderResult<()> {
1354 let start = Instant::now();
1355 self.ensure_no_queued_prune()?;
1356
1357 debug_assert!(self.writer.user_header().segment() == StaticFileSegment::TransactionSenders);
1358 self.append_with_tx_number(tx_num, sender)?;
1359
1360 if let Some(metrics) = &self.metrics {
1361 metrics.record_segment_operation(
1362 StaticFileSegment::TransactionSenders,
1363 StaticFileProviderOperation::Append,
1364 Some(start.elapsed()),
1365 );
1366 }
1367
1368 Ok(())
1369 }
1370
1371 pub fn append_transaction_senders<I>(&mut self, senders: I) -> ProviderResult<()>
1373 where
1374 I: Iterator<Item = (TxNumber, alloy_primitives::Address)>,
1375 {
1376 debug_assert!(self.writer.user_header().segment() == StaticFileSegment::TransactionSenders);
1377
1378 let mut senders_iter = senders.into_iter().peekable();
1379 if senders_iter.peek().is_none() {
1381 return Ok(());
1382 }
1383
1384 let start = Instant::now();
1385 self.ensure_no_queued_prune()?;
1386
1387 let mut count: u64 = 0;
1389 for (tx_num, sender) in senders_iter {
1390 self.append_with_tx_number(tx_num, sender)?;
1391 count += 1;
1392 }
1393
1394 if let Some(metrics) = &self.metrics {
1395 metrics.record_segment_operations(
1396 StaticFileSegment::TransactionSenders,
1397 StaticFileProviderOperation::Append,
1398 count,
1399 Some(start.elapsed()),
1400 );
1401 }
1402
1403 Ok(())
1404 }
1405
1406 pub fn append_account_changeset(
1412 &mut self,
1413 mut changeset: Vec<AccountBeforeTx>,
1414 block_number: u64,
1415 ) -> ProviderResult<()> {
1416 debug_assert!(self.writer.user_header().segment() == StaticFileSegment::AccountChangeSets);
1417 let start = Instant::now();
1418
1419 self.increment_block(block_number)?;
1420 self.ensure_no_queued_prune()?;
1421
1422 changeset.sort_by_key(|change| change.address);
1424
1425 let mut count: u64 = 0;
1426
1427 for change in changeset {
1428 self.append_change(&change)?;
1429 count += 1;
1430 }
1431
1432 if let Some(metrics) = &self.metrics {
1433 metrics.record_segment_operations(
1434 StaticFileSegment::AccountChangeSets,
1435 StaticFileProviderOperation::Append,
1436 count,
1437 Some(start.elapsed()),
1438 );
1439 }
1440
1441 Ok(())
1442 }
1443
1444 pub fn begin_account_changeset(&mut self, block_number: u64) -> ProviderResult<()> {
1449 debug_assert!(self.writer.user_header().segment() == StaticFileSegment::AccountChangeSets);
1450
1451 self.increment_block(block_number)?;
1452 self.ensure_no_queued_prune()
1453 }
1454
1455 pub fn append_account_changeset_entry(
1459 &mut self,
1460 change: AccountBeforeTx,
1461 ) -> ProviderResult<()> {
1462 debug_assert!(self.writer.user_header().segment() == StaticFileSegment::AccountChangeSets);
1463 if self.current_changeset_offset.is_none() {
1464 return Err(ProviderError::other(StaticFileWriterError::new(
1465 "account changeset stream must be started before appending entries",
1466 )))
1467 }
1468
1469 self.append_change(&change)
1470 }
1471
1472 pub fn append_storage_changeset(
1476 &mut self,
1477 mut changeset: Vec<StorageBeforeTx>,
1478 block_number: u64,
1479 ) -> ProviderResult<()> {
1480 debug_assert!(self.writer.user_header().segment() == StaticFileSegment::StorageChangeSets);
1481 let start = Instant::now();
1482
1483 self.increment_block(block_number)?;
1484 self.ensure_no_queued_prune()?;
1485
1486 changeset.sort_by_key(|change| (change.address, change.key));
1488
1489 let mut count: u64 = 0;
1490 for change in changeset {
1491 self.append_change(&change)?;
1492 count += 1;
1493 }
1494
1495 if let Some(metrics) = &self.metrics {
1496 metrics.record_segment_operations(
1497 StaticFileSegment::StorageChangeSets,
1498 StaticFileProviderOperation::Append,
1499 count,
1500 Some(start.elapsed()),
1501 );
1502 }
1503
1504 Ok(())
1505 }
1506
1507 pub fn begin_storage_changeset(&mut self, block_number: u64) -> ProviderResult<()> {
1513 debug_assert!(self.writer.user_header().segment() == StaticFileSegment::StorageChangeSets);
1514
1515 self.increment_block(block_number)?;
1516 self.ensure_no_queued_prune()
1517 }
1518
1519 pub fn append_storage_changeset_entry(
1523 &mut self,
1524 change: StorageBeforeTx,
1525 ) -> ProviderResult<()> {
1526 debug_assert!(self.writer.user_header().segment() == StaticFileSegment::StorageChangeSets);
1527 if self.current_changeset_offset.is_none() {
1528 return Err(ProviderError::other(StaticFileWriterError::new(
1529 "storage changeset stream must be started before appending entries",
1530 )))
1531 }
1532
1533 self.append_change(&change)
1534 }
1535
1536 pub fn prune_transactions(
1540 &mut self,
1541 to_delete: u64,
1542 last_block: BlockNumber,
1543 ) -> ProviderResult<()> {
1544 debug_assert_eq!(self.writer.user_header().segment(), StaticFileSegment::Transactions);
1545 self.queue_prune(PruneStrategy::Transactions { num_rows: to_delete, last_block })
1546 }
1547
1548 pub fn prune_receipts(
1552 &mut self,
1553 to_delete: u64,
1554 last_block: BlockNumber,
1555 ) -> ProviderResult<()> {
1556 debug_assert_eq!(self.writer.user_header().segment(), StaticFileSegment::Receipts);
1557 self.queue_prune(PruneStrategy::Receipts { num_rows: to_delete, last_block })
1558 }
1559
1560 pub fn prune_transaction_senders(
1564 &mut self,
1565 to_delete: u64,
1566 last_block: BlockNumber,
1567 ) -> ProviderResult<()> {
1568 debug_assert_eq!(
1569 self.writer.user_header().segment(),
1570 StaticFileSegment::TransactionSenders
1571 );
1572 self.queue_prune(PruneStrategy::TransactionSenders { num_rows: to_delete, last_block })
1573 }
1574
1575 pub fn prune_headers(&mut self, to_delete: u64) -> ProviderResult<()> {
1577 debug_assert_eq!(self.writer.user_header().segment(), StaticFileSegment::Headers);
1578 self.queue_prune(PruneStrategy::Headers { num_blocks: to_delete })
1579 }
1580
1581 pub fn prune_account_changesets(&mut self, last_block: u64) -> ProviderResult<()> {
1583 debug_assert_eq!(self.writer.user_header().segment(), StaticFileSegment::AccountChangeSets);
1584 self.queue_prune(PruneStrategy::AccountChangeSets { last_block })
1585 }
1586
1587 pub fn prune_storage_changesets(&mut self, last_block: u64) -> ProviderResult<()> {
1589 debug_assert_eq!(self.writer.user_header().segment(), StaticFileSegment::StorageChangeSets);
1590 self.queue_prune(PruneStrategy::StorageChangeSets { last_block })
1591 }
1592
1593 fn queue_prune(&mut self, strategy: PruneStrategy) -> ProviderResult<()> {
1595 self.ensure_no_queued_prune()?;
1596 match strategy {
1598 PruneStrategy::Transactions { last_block, .. } |
1599 PruneStrategy::Receipts { last_block, .. } |
1600 PruneStrategy::TransactionSenders { last_block, .. } |
1601 PruneStrategy::AccountChangeSets { last_block } |
1602 PruneStrategy::StorageChangeSets { last_block } => {
1603 self.ensure_above_pruned_anchor(last_block)?;
1604 }
1605 PruneStrategy::Headers { .. } => {}
1606 }
1607 self.prune_on_commit = Some(strategy);
1608 Ok(())
1609 }
1610
1611 fn ensure_no_queued_prune(&self) -> ProviderResult<()> {
1613 if self.prune_on_commit.is_some() {
1614 return Err(ProviderError::other(StaticFileWriterError::new(
1615 "Pruning should be committed before appending or pruning more data",
1616 )));
1617 }
1618 Ok(())
1619 }
1620
1621 fn prune_transaction_data(
1623 &mut self,
1624 to_delete: u64,
1625 last_block: BlockNumber,
1626 ) -> ProviderResult<()> {
1627 let start = Instant::now();
1628
1629 debug_assert!(self.writer.user_header().segment() == StaticFileSegment::Transactions);
1630
1631 self.truncate(to_delete, Some(last_block))?;
1632
1633 if let Some(metrics) = &self.metrics {
1634 metrics.record_segment_operation(
1635 StaticFileSegment::Transactions,
1636 StaticFileProviderOperation::Prune,
1637 Some(start.elapsed()),
1638 );
1639 }
1640
1641 Ok(())
1642 }
1643
1644 fn prune_account_changeset_data(&mut self, last_block: BlockNumber) -> ProviderResult<()> {
1646 let start = Instant::now();
1647
1648 debug_assert!(self.writer.user_header().segment() == StaticFileSegment::AccountChangeSets);
1649
1650 self.truncate_changesets(last_block)?;
1651
1652 if let Some(metrics) = &self.metrics {
1653 metrics.record_segment_operation(
1654 StaticFileSegment::AccountChangeSets,
1655 StaticFileProviderOperation::Prune,
1656 Some(start.elapsed()),
1657 );
1658 }
1659
1660 Ok(())
1661 }
1662
1663 fn prune_storage_changeset_data(&mut self, last_block: BlockNumber) -> ProviderResult<()> {
1665 let start = Instant::now();
1666
1667 debug_assert!(self.writer.user_header().segment() == StaticFileSegment::StorageChangeSets);
1668
1669 self.truncate_changesets(last_block)?;
1670
1671 if let Some(metrics) = &self.metrics {
1672 metrics.record_segment_operation(
1673 StaticFileSegment::StorageChangeSets,
1674 StaticFileProviderOperation::Prune,
1675 Some(start.elapsed()),
1676 );
1677 }
1678
1679 Ok(())
1680 }
1681
1682 fn prune_receipt_data(
1684 &mut self,
1685 to_delete: u64,
1686 last_block: BlockNumber,
1687 ) -> ProviderResult<()> {
1688 let start = Instant::now();
1689
1690 debug_assert!(self.writer.user_header().segment() == StaticFileSegment::Receipts);
1691
1692 self.truncate(to_delete, Some(last_block))?;
1693
1694 if let Some(metrics) = &self.metrics {
1695 metrics.record_segment_operation(
1696 StaticFileSegment::Receipts,
1697 StaticFileProviderOperation::Prune,
1698 Some(start.elapsed()),
1699 );
1700 }
1701
1702 Ok(())
1703 }
1704
1705 fn prune_transaction_sender_data(
1707 &mut self,
1708 to_delete: u64,
1709 last_block: BlockNumber,
1710 ) -> ProviderResult<()> {
1711 let start = Instant::now();
1712
1713 debug_assert!(self.writer.user_header().segment() == StaticFileSegment::TransactionSenders);
1714
1715 self.truncate(to_delete, Some(last_block))?;
1716
1717 if let Some(metrics) = &self.metrics {
1718 metrics.record_segment_operation(
1719 StaticFileSegment::TransactionSenders,
1720 StaticFileProviderOperation::Prune,
1721 Some(start.elapsed()),
1722 );
1723 }
1724
1725 Ok(())
1726 }
1727
1728 fn prune_header_data(&mut self, to_delete: u64) -> ProviderResult<()> {
1730 let start = Instant::now();
1731
1732 debug_assert!(self.writer.user_header().segment() == StaticFileSegment::Headers);
1733
1734 self.truncate(to_delete, None)?;
1735
1736 if let Some(metrics) = &self.metrics {
1737 metrics.record_segment_operation(
1738 StaticFileSegment::Headers,
1739 StaticFileProviderOperation::Prune,
1740 Some(start.elapsed()),
1741 );
1742 }
1743
1744 Ok(())
1745 }
1746
1747 pub fn reader(&self) -> StaticFileProvider<N> {
1749 Self::upgrade_provider_to_strong_reference(&self.reader)
1750 }
1751
1752 fn upgrade_provider_to_strong_reference(
1761 provider: &Weak<StaticFileProviderInner<N>>,
1762 ) -> StaticFileProvider<N> {
1763 provider.upgrade().map(StaticFileProvider).expect("StaticFileProvider is dropped")
1764 }
1765
1766 pub const fn user_header(&self) -> &SegmentHeader {
1768 self.writer.user_header()
1769 }
1770
1771 pub const fn user_header_mut(&mut self) -> &mut SegmentHeader {
1773 self.writer.user_header_mut()
1774 }
1775
1776 #[cfg(any(test, feature = "test-utils"))]
1778 pub const fn set_block_range(&mut self, block_range: std::ops::RangeInclusive<BlockNumber>) {
1779 self.writer.user_header_mut().set_block_range(*block_range.start(), *block_range.end())
1780 }
1781
1782 #[cfg(any(test, feature = "test-utils"))]
1784 pub const fn inner(&mut self) -> &mut NippyJarWriter<SegmentHeader> {
1785 &mut self.writer
1786 }
1787}
1788
1789fn create_jar(
1790 segment: StaticFileSegment,
1791 path: &Path,
1792 expected_block_range: SegmentRangeInclusive,
1793) -> NippyJar<SegmentHeader> {
1794 let mut jar = NippyJar::new(
1795 segment.columns(),
1796 path,
1797 SegmentHeader::new(expected_block_range, None, None, segment),
1798 );
1799
1800 if segment.is_headers() {
1803 jar = jar.with_lz4();
1804 }
1805
1806 jar
1807}