mirror of https://github.com/dswd/zvault
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
96 lines
3.6 KiB
96 lines
3.6 KiB
use ::prelude::*; |
|
|
|
use std::collections::HashSet; |
|
|
|
|
|
impl Repository { |
|
fn delete_bundle(&mut self, id: u32) -> Result<(), RepositoryError> { |
|
if let Some(bundle) = self.bundle_map.remove(id) { |
|
try!(self.bundles.delete_bundle(&bundle)); |
|
Ok(()) |
|
} else { |
|
Err(IntegrityError::MissingBundleId(id).into()) |
|
} |
|
} |
|
|
|
pub fn vacuum(&mut self, ratio: f32, combine: bool, force: bool) -> Result<(), RepositoryError> { |
|
try!(self.flush()); |
|
info!("Locking repository"); |
|
try!(self.write_mode()); |
|
let _lock = try!(self.lock(true)); |
|
// analyze_usage will set the dirty flag |
|
info!("Analyzing chunk usage"); |
|
let usage = try!(self.analyze_usage()); |
|
let mut data_total = 0; |
|
let mut data_used = 0; |
|
for bundle in usage.values() { |
|
data_total += bundle.info.encoded_size; |
|
data_used += bundle.get_used_size(); |
|
} |
|
info!("Usage: {} of {}, {:.1}%", to_file_size(data_used as u64), to_file_size(data_total as u64), data_used as f32/data_total as f32*100.0); |
|
let mut rewrite_bundles = HashSet::new(); |
|
let mut reclaim_space = 0; |
|
for (id, bundle) in &usage { |
|
if bundle.get_usage_ratio() <= ratio { |
|
rewrite_bundles.insert(*id); |
|
reclaim_space += bundle.get_unused_size(); |
|
} |
|
} |
|
if combine { |
|
let mut small_meta = vec![]; |
|
let mut small_data = vec![]; |
|
for (id, bundle) in &usage { |
|
if bundle.info.encoded_size * 4 < self.config.bundle_size { |
|
match bundle.info.mode { |
|
BundleMode::Meta => small_meta.push(*id), |
|
BundleMode::Data => small_data.push(*id), |
|
} |
|
} |
|
} |
|
if small_meta.len() >= 2 { |
|
for bundle in small_meta { |
|
rewrite_bundles.insert(bundle); |
|
} |
|
} |
|
if small_data.len() >= 2 { |
|
for bundle in small_data { |
|
rewrite_bundles.insert(bundle); |
|
} |
|
} |
|
} |
|
info!("Reclaiming {} by rewriting {} bundles", to_file_size(reclaim_space as u64), rewrite_bundles.len()); |
|
if !force { |
|
self.dirty = false; |
|
return Ok(()) |
|
} |
|
for id in ProgressIter::new("rewriting bundles", rewrite_bundles.len(), rewrite_bundles.iter()) { |
|
let bundle = &usage[id]; |
|
let bundle_id = self.bundle_map.get(*id).unwrap(); |
|
let chunks = try!(self.bundles.get_chunk_list(&bundle_id)); |
|
let mode = usage[id].info.mode; |
|
for (chunk, &(hash, _len)) in chunks.into_iter().enumerate() { |
|
if !bundle.chunk_usage.get(chunk) { |
|
try!(self.index.delete(&hash)); |
|
continue |
|
} |
|
let data = try!(self.bundles.get_chunk(&bundle_id, chunk)); |
|
try!(self.put_chunk_override(mode, hash, &data)); |
|
} |
|
} |
|
try!(self.flush()); |
|
info!("Checking index"); |
|
self.index.walk::<_, ()>(|hash, location| { |
|
if rewrite_bundles.contains(&location.bundle) { |
|
panic!("Removed bundle is still referenced in index: hash:{}, bundle:{}, chunk:{}", hash, location.bundle, location.chunk); |
|
} |
|
Ok(()) |
|
}).ok(); |
|
info!("Deleting {} bundles", rewrite_bundles.len()); |
|
for id in rewrite_bundles { |
|
try!(self.delete_bundle(id)); |
|
} |
|
try!(self.save_bundle_map()); |
|
self.dirty = false; |
|
Ok(()) |
|
} |
|
}
|
|
|