use crate::peer_handler::DumpError;
use crate::snap::constants::CODE_HASH_WRITE_BUFFER_SIZE;
use crate::sync::SyncError;
use crate::utils::{dump_to_file, get_code_hashes_snapshot_file};
use ethrex_common::H256;
use ethrex_rlp::encode::RLPEncode;
use std::collections::HashSet;
use std::path::PathBuf;
use tokio::task::JoinSet;
use tracing::error;
pub struct CodeHashCollector {
buffer: HashSet<H256>,
snapshots_dir: PathBuf,
file_index: u64,
disk_tasks: JoinSet<Result<(), DumpError>>,
}
impl CodeHashCollector {
pub fn new(snapshots_dir: PathBuf) -> Self {
Self {
buffer: HashSet::new(),
snapshots_dir,
file_index: 0,
disk_tasks: JoinSet::new(),
}
}
pub fn add(&mut self, hash: H256) {
self.buffer.insert(hash);
}
#[cfg(not(feature = "rocksdb"))]
pub fn extend(&mut self, hashes: impl IntoIterator<Item = H256>) {
self.buffer.extend(hashes);
}
pub async fn flush_if_needed(&mut self) -> Result<(), SyncError> {
if self.buffer.len() >= CODE_HASH_WRITE_BUFFER_SIZE {
self.check_previous_task().await?;
let buffer = std::mem::take(&mut self.buffer);
self.flush_buffer(buffer);
}
Ok(())
}
pub async fn finish(mut self) -> Result<(), SyncError> {
if !self.buffer.is_empty() {
let buffer = std::mem::take(&mut self.buffer);
self.flush_buffer(buffer);
}
self.disk_tasks
.join_all()
.await
.into_iter()
.map(|result| {
result.inspect_err(|err| {
error!("Failed final write for code hashes: {err:?}");
})
})
.collect::<Result<Vec<()>, DumpError>>()
.map_err(|_| SyncError::BytecodeFileError)?;
Ok(())
}
fn flush_buffer(&mut self, buffer: HashSet<H256>) {
let file_name = get_code_hashes_snapshot_file(&self.snapshots_dir, self.file_index);
let encoded = buffer.into_iter().collect::<Vec<_>>().encode_to_vec();
self.disk_tasks
.spawn(async move { dump_to_file(&file_name, encoded) });
self.file_index += 1;
}
async fn check_previous_task(&mut self) -> Result<(), SyncError> {
if let Some(task) = self.disk_tasks.join_next().await {
task?
.inspect_err(|err| error!("Error when dumping code hashes to file: {err:?}"))
.map_err(|_| SyncError::BytecodeFileError)?;
}
Ok(())
}
}