Deep checking and vacuuming
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
This commit is contained in:
parent
9fff39d66a
commit
4b2afc261c
@ -1 +1,223 @@
|
|||||||
|
use crate::chunking::RecursiveUnchunker;
|
||||||
|
use crate::commands::retrieve_tree_node;
|
||||||
|
use crate::definitions::{ChunkId, TreeNode};
|
||||||
|
use crate::pile::{Keyspace, Pile, RawPile};
|
||||||
|
use anyhow::bail;
|
||||||
|
use indicatif::{ProgressBar, ProgressDrawTarget, ProgressStyle};
|
||||||
|
use log::{error, info, warn};
|
||||||
|
use std::collections::HashSet;
|
||||||
|
use std::io::Write;
|
||||||
|
use std::sync::Mutex;
|
||||||
|
|
||||||
|
#[derive(PartialEq, Eq, Copy, Clone, Debug)]
|
||||||
|
pub enum VacuumMode {
|
||||||
|
NoVacuum,
|
||||||
|
DryRunVacuum,
|
||||||
|
Vacuum,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct NullWriter {}
|
||||||
|
|
||||||
|
impl Write for NullWriter {
|
||||||
|
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
|
||||||
|
Ok(buf.len())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn flush(&mut self) -> std::io::Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Mark-and-sweep style vacuuming system.
|
||||||
|
pub struct VacuumRawPile<RP: RawPile> {
|
||||||
|
underlying: RP,
|
||||||
|
vacuum_tracking_enabled: bool,
|
||||||
|
retrieved_chunks: Mutex<HashSet<ChunkId>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<RP: RawPile> VacuumRawPile<RP> {
|
||||||
|
pub fn new(underlying: RP, vacuum_tracking_enabled: bool) -> Self {
|
||||||
|
VacuumRawPile {
|
||||||
|
underlying,
|
||||||
|
vacuum_tracking_enabled,
|
||||||
|
retrieved_chunks: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn calculate_vacuum_for_sweeping(&self) -> anyhow::Result<HashSet<ChunkId>> {
|
||||||
|
if !self.vacuum_tracking_enabled {
|
||||||
|
bail!("Vacuum tracking not enabled, you can't calculate the vacuum set!");
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut to_sweep = HashSet::new();
|
||||||
|
|
||||||
|
let retrieved_chunks = self.retrieved_chunks.lock().unwrap();
|
||||||
|
|
||||||
|
let mut chunk_id: ChunkId = Default::default();
|
||||||
|
for key in self.list_keys(Keyspace::Chunk)? {
|
||||||
|
chunk_id.clone_from_slice(&key?);
|
||||||
|
if !retrieved_chunks.contains(&chunk_id) {
|
||||||
|
to_sweep.insert(chunk_id.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(to_sweep)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<RP: RawPile> RawPile for VacuumRawPile<RP> {
|
||||||
|
fn exists(&self, kind: Keyspace, key: &[u8]) -> anyhow::Result<bool> {
|
||||||
|
self.underlying.exists(kind, key)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read(&self, kind: Keyspace, key: &[u8]) -> anyhow::Result<Option<Vec<u8>>> {
|
||||||
|
self.underlying.read(kind, key)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write(&self, kind: Keyspace, key: &[u8], value: &[u8]) -> anyhow::Result<()> {
|
||||||
|
if self.vacuum_tracking_enabled && kind == Keyspace::Chunk {
|
||||||
|
let mut chunk_id: ChunkId = Default::default();
|
||||||
|
chunk_id.clone_from_slice(key);
|
||||||
|
self.retrieved_chunks.lock().unwrap().insert(chunk_id);
|
||||||
|
}
|
||||||
|
self.underlying.write(kind, key, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn delete(&self, kind: Keyspace, key: &[u8]) -> anyhow::Result<()> {
|
||||||
|
self.underlying.delete(kind, key)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn list_keys(
|
||||||
|
&self,
|
||||||
|
kind: Keyspace,
|
||||||
|
) -> anyhow::Result<Box<dyn Iterator<Item = anyhow::Result<Vec<u8>>>>> {
|
||||||
|
self.underlying.list_keys(kind)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn flush(&self) -> anyhow::Result<()> {
|
||||||
|
self.underlying.flush()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_lowlevel(&self) -> anyhow::Result<bool> {
|
||||||
|
self.underlying.check_lowlevel()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn check_deep<RP: RawPile>(
|
||||||
|
pile: Pile<RP>,
|
||||||
|
vacuum: VacuumMode,
|
||||||
|
make_progress_bar: bool,
|
||||||
|
) -> anyhow::Result<u32> {
|
||||||
|
let pile = Pile::new(VacuumRawPile::new(
|
||||||
|
pile.raw_pile,
|
||||||
|
vacuum != VacuumMode::NoVacuum,
|
||||||
|
));
|
||||||
|
|
||||||
|
let mut errors = 0;
|
||||||
|
|
||||||
|
let mut to_check = Vec::new();
|
||||||
|
let pointer_list = pile.list_pointers()?;
|
||||||
|
|
||||||
|
for pointer in pointer_list.iter() {
|
||||||
|
info!("Checking pointer {:?}", pointer);
|
||||||
|
match pile.read_pointer(&pointer)? {
|
||||||
|
Some(pointer_data) => {
|
||||||
|
if let Some(parent) = pointer_data.parent_pointer {
|
||||||
|
if !pointer_list.contains(&parent) {
|
||||||
|
errors += 1;
|
||||||
|
error!(
|
||||||
|
"Pointer {:?} has a parent {:?} which does not exist.",
|
||||||
|
pointer, parent
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let tree_node = retrieve_tree_node(&pile, pointer_data.chunk_ref.clone())?;
|
||||||
|
tree_node.node.visit(
|
||||||
|
&mut |node, _| {
|
||||||
|
if let TreeNode::NormalFile { content, .. } = node {
|
||||||
|
to_check.push(content.clone());
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
},
|
||||||
|
"".to_owned(),
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
errors += 1;
|
||||||
|
error!("Pointer {:?} does not seem to exist.", pointer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let pbar = if make_progress_bar {
|
||||||
|
ProgressBar::with_draw_target(1000 as u64, ProgressDrawTarget::stdout_with_hz(10))
|
||||||
|
} else {
|
||||||
|
ProgressBar::hidden()
|
||||||
|
};
|
||||||
|
pbar.set_style(
|
||||||
|
ProgressStyle::default_bar()
|
||||||
|
.template("[{elapsed_precise}]/[{eta}] {bar:40.cyan/blue} {pos:>7}/{len:7} {msg}"),
|
||||||
|
);
|
||||||
|
pbar.set_message("checking");
|
||||||
|
|
||||||
|
let mut done = 0;
|
||||||
|
|
||||||
|
while let Some(next_to_check) = to_check.pop() {
|
||||||
|
done += 1;
|
||||||
|
pbar.set_length(done + to_check.len() as u64);
|
||||||
|
pbar.set_position(done);
|
||||||
|
|
||||||
|
let mut unchunker = RecursiveUnchunker::new(&pile, next_to_check.clone());
|
||||||
|
match std::io::copy(&mut unchunker, &mut NullWriter {}) {
|
||||||
|
Ok(_) => {}
|
||||||
|
Err(err) => {
|
||||||
|
errors += 1;
|
||||||
|
warn!(
|
||||||
|
"Error occurred when reading {:?}: {:?}.",
|
||||||
|
next_to_check, err
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pbar.finish_and_clear();
|
||||||
|
|
||||||
|
if errors > 0 {
|
||||||
|
error!("There were {:?}", errors);
|
||||||
|
} else {
|
||||||
|
info!("No errors.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if errors == 0 && vacuum != VacuumMode::NoVacuum {
|
||||||
|
info!("Calculating sweep set for vacuuming.");
|
||||||
|
let to_vacuum = pile.raw_pile.calculate_vacuum_for_sweeping()?;
|
||||||
|
info!("{} chunks are ready to be vacuumed.", to_vacuum.len());
|
||||||
|
if vacuum == VacuumMode::Vacuum {
|
||||||
|
let pbar = if make_progress_bar {
|
||||||
|
ProgressBar::with_draw_target(
|
||||||
|
to_vacuum.len() as u64,
|
||||||
|
ProgressDrawTarget::stdout_with_hz(10),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
ProgressBar::hidden()
|
||||||
|
};
|
||||||
|
pbar.set_style(
|
||||||
|
ProgressStyle::default_bar().template(
|
||||||
|
"[{elapsed_precise}]/[{eta}] {bar:40.cyan/blue} {pos:>7}/{len:7} {msg}",
|
||||||
|
),
|
||||||
|
);
|
||||||
|
pbar.set_message("vacuuming");
|
||||||
|
|
||||||
|
// actually do the vacuum!
|
||||||
|
info!("Going to vacuum them up.");
|
||||||
|
for vacuum_id in to_vacuum {
|
||||||
|
pile.raw_pile.delete(Keyspace::Chunk, &vacuum_id)?;
|
||||||
|
pbar.inc(1);
|
||||||
|
}
|
||||||
|
pbar.finish_and_clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(errors)
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user