[thin_shrink] add a trivial snapshot test.
Still need much more work on snap tests.
This commit is contained in:
		@@ -1,8 +1,10 @@
 | 
				
			|||||||
use anyhow::{anyhow, Result};
 | 
					use anyhow::{anyhow, Result};
 | 
				
			||||||
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
 | 
					use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
 | 
				
			||||||
use rand::prelude::*;
 | 
					use rand::prelude::*;
 | 
				
			||||||
 | 
					use std::collections::VecDeque;
 | 
				
			||||||
use std::fs::OpenOptions;
 | 
					use std::fs::OpenOptions;
 | 
				
			||||||
use std::io::{Cursor, Read, Seek, SeekFrom, Write};
 | 
					use std::io::{Cursor, Read, Seek, SeekFrom, Write};
 | 
				
			||||||
 | 
					use std::ops::Range;
 | 
				
			||||||
use std::path::{Path, PathBuf};
 | 
					use std::path::{Path, PathBuf};
 | 
				
			||||||
use tempfile::tempdir;
 | 
					use tempfile::tempdir;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -603,7 +605,282 @@ fn shrink_fragmented_thin_64() -> Result<()> {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
//------------------------------------
 | 
					//------------------------------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					struct Allocator {
 | 
				
			||||||
 | 
					    runs: VecDeque<Range<u64>>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Allocator {
 | 
				
			||||||
 | 
					    fn new_shuffled(total_len: u64, run_len: Range<u64>) -> Allocator {
 | 
				
			||||||
 | 
					        let mut runs = Vec::new();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let mut b = 0u64;
 | 
				
			||||||
 | 
					        while b < total_len {
 | 
				
			||||||
 | 
					            let len = u64::min(
 | 
				
			||||||
 | 
					                total_len - b,
 | 
				
			||||||
 | 
					                thread_rng().gen_range(run_len.start, run_len.end),
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					            runs.push(b..(b + len));
 | 
				
			||||||
 | 
					            b += len;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        runs.shuffle(&mut thread_rng());
 | 
				
			||||||
 | 
					        let runs: VecDeque<Range<u64>> = runs.iter().map(|r| r.clone()).collect();
 | 
				
			||||||
 | 
					        Allocator { runs }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn is_empty(&self) -> bool {
 | 
				
			||||||
 | 
					        self.runs.is_empty()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn alloc(&mut self, len: u64) -> Result<Vec<Range<u64>>> {
 | 
				
			||||||
 | 
					        let mut len = len;
 | 
				
			||||||
 | 
					        let mut runs = Vec::new();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        while len > 0 {
 | 
				
			||||||
 | 
					            let r = self.runs.pop_front();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if r.is_none() {
 | 
				
			||||||
 | 
					                return Err(anyhow!("could not allocate; out of space"));
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            let mut r = r.unwrap();
 | 
				
			||||||
 | 
					            let rlen = r.end - r.start;
 | 
				
			||||||
 | 
					            if len < rlen {
 | 
				
			||||||
 | 
					                runs.push(r.start..(r.start + len));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // We need to push something back.
 | 
				
			||||||
 | 
					                self.runs.push_front((r.start + len)..r.end);
 | 
				
			||||||
 | 
					                len = 0;
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                runs.push(r.start..r.end);
 | 
				
			||||||
 | 
					                len -= rlen;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Ok(runs)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Having explicitly unmapped regions makes it easier to
 | 
				
			||||||
 | 
					// apply snapshots.
 | 
				
			||||||
 | 
					#[derive(Clone)]
 | 
				
			||||||
 | 
					enum Run {
 | 
				
			||||||
 | 
					    Mapped { data_begin: u64, len: u64 },
 | 
				
			||||||
 | 
					    UnMapped { len: u64 },
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Run {
 | 
				
			||||||
 | 
					    fn len(&self) -> u64 {
 | 
				
			||||||
 | 
					        match self {
 | 
				
			||||||
 | 
					            Run::Mapped {
 | 
				
			||||||
 | 
					                data_begin: _data_begin,
 | 
				
			||||||
 | 
					                len,
 | 
				
			||||||
 | 
					            } => *len,
 | 
				
			||||||
 | 
					            Run::UnMapped { len } => *len,
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn split(&self, n: u64) -> (Option<Run>, Option<Run>) {
 | 
				
			||||||
 | 
					        if n == 0 {
 | 
				
			||||||
 | 
					            return (None, Some(self.clone()));
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            if self.len() <= n {
 | 
				
			||||||
 | 
					                return (Some(self.clone()), None);
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                match self {
 | 
				
			||||||
 | 
					                    Run::Mapped { data_begin, len } => (
 | 
				
			||||||
 | 
					                        Some(Run::Mapped {
 | 
				
			||||||
 | 
					                            data_begin: *data_begin,
 | 
				
			||||||
 | 
					                            len: n,
 | 
				
			||||||
 | 
					                        }),
 | 
				
			||||||
 | 
					                        Some(Run::Mapped {
 | 
				
			||||||
 | 
					                            data_begin: data_begin + n,
 | 
				
			||||||
 | 
					                            len: len - n,
 | 
				
			||||||
 | 
					                        }),
 | 
				
			||||||
 | 
					                    ),
 | 
				
			||||||
 | 
					                    Run::UnMapped { len } => (
 | 
				
			||||||
 | 
					                        Some(Run::UnMapped { len: n }),
 | 
				
			||||||
 | 
					                        Some(Run::UnMapped { len: len - n }),
 | 
				
			||||||
 | 
					                    ),
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Clone)]
 | 
				
			||||||
 | 
					struct ThinDev {
 | 
				
			||||||
 | 
					    thin_id: u32,
 | 
				
			||||||
 | 
					    dev_size: u64,
 | 
				
			||||||
 | 
					    runs: Vec<Run>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl ThinDev {
 | 
				
			||||||
 | 
					    fn emit(&self, v: &mut dyn xml::MetadataVisitor) -> Result<()> {
 | 
				
			||||||
 | 
					        v.device_b(&xml::Device {
 | 
				
			||||||
 | 
					            dev_id: self.thin_id,
 | 
				
			||||||
 | 
					            mapped_blocks: self.dev_size,
 | 
				
			||||||
 | 
					            transaction: 0,
 | 
				
			||||||
 | 
					            creation_time: 0,
 | 
				
			||||||
 | 
					            snap_time: 0,
 | 
				
			||||||
 | 
					        })?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let mut b = 0;
 | 
				
			||||||
 | 
					        for r in &self.runs {
 | 
				
			||||||
 | 
					            match r {
 | 
				
			||||||
 | 
					                Run::Mapped { data_begin, len } => {
 | 
				
			||||||
 | 
					                    v.map(&xml::Map {
 | 
				
			||||||
 | 
					                        thin_begin: b,
 | 
				
			||||||
 | 
					                        data_begin: *data_begin,
 | 
				
			||||||
 | 
					                        time: 0,
 | 
				
			||||||
 | 
					                        len: *len,
 | 
				
			||||||
 | 
					                    })?;
 | 
				
			||||||
 | 
					                    b += len;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                Run::UnMapped { len } => {
 | 
				
			||||||
 | 
					                    b += len;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        v.device_e()?;
 | 
				
			||||||
 | 
					        Ok(())
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Clone)]
 | 
				
			||||||
 | 
					enum SnapRunType {
 | 
				
			||||||
 | 
					    Same,
 | 
				
			||||||
 | 
					    Diff,
 | 
				
			||||||
 | 
					    Hole,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Clone)]
 | 
				
			||||||
 | 
					struct SnapRun(SnapRunType, u64);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn mk_origin(thin_id: u32, total_len: u64, allocator: &mut Allocator) -> Result<ThinDev> {
 | 
				
			||||||
 | 
					    let mut runs = Vec::new();
 | 
				
			||||||
 | 
					    let mut b = 0;
 | 
				
			||||||
 | 
					    while b < total_len {
 | 
				
			||||||
 | 
					        let len = u64::min(thread_rng().gen_range(16, 64), total_len - b);
 | 
				
			||||||
 | 
					        match thread_rng().gen_range(0, 2) {
 | 
				
			||||||
 | 
					            0 => {
 | 
				
			||||||
 | 
					                for data in allocator.alloc(len)? {
 | 
				
			||||||
 | 
					                    assert!(data.end >= data.start);
 | 
				
			||||||
 | 
					                    runs.push(Run::Mapped {
 | 
				
			||||||
 | 
					                        data_begin: data.start,
 | 
				
			||||||
 | 
					                        len: data.end - data.start,
 | 
				
			||||||
 | 
					                    });
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            1 => {
 | 
				
			||||||
 | 
					                runs.push(Run::UnMapped { len });
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            _ => {
 | 
				
			||||||
 | 
					                return Err(anyhow!("bad value returned from rng"));
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        b += len;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Ok(ThinDev {
 | 
				
			||||||
 | 
					        thin_id,
 | 
				
			||||||
 | 
					        dev_size: total_len,
 | 
				
			||||||
 | 
					        runs,
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn mk_snap_mapping(
 | 
				
			||||||
 | 
					    total_len: u64,
 | 
				
			||||||
 | 
					    run_len: Range<u64>,
 | 
				
			||||||
 | 
					    same_percent: usize,
 | 
				
			||||||
 | 
					    diff_percent: usize,
 | 
				
			||||||
 | 
					) -> Vec<SnapRun> {
 | 
				
			||||||
 | 
					    let mut runs = Vec::new();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let mut b = 0u64;
 | 
				
			||||||
 | 
					    while b < total_len {
 | 
				
			||||||
 | 
					        let len = u64::min(
 | 
				
			||||||
 | 
					            total_len - b,
 | 
				
			||||||
 | 
					            thread_rng().gen_range(run_len.start, run_len.end),
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let n = thread_rng().gen_range(0, 100);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if n < same_percent {
 | 
				
			||||||
 | 
					            runs.push(SnapRun(SnapRunType::Same, len));
 | 
				
			||||||
 | 
					        } else if n < diff_percent {
 | 
				
			||||||
 | 
					            runs.push(SnapRun(SnapRunType::Diff, len));
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            runs.push(SnapRun(SnapRunType::Hole, len));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        b += len;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    runs
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn split_runs(mut n: u64, runs: &Vec<Run>) -> (Vec<Run>, Vec<Run>) {
 | 
				
			||||||
 | 
					    let mut before = Vec::new();
 | 
				
			||||||
 | 
					    let mut after = Vec::new();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for r in runs {
 | 
				
			||||||
 | 
					        match r.split(n) {
 | 
				
			||||||
 | 
					            (Some(lhs), None) => {
 | 
				
			||||||
 | 
					                before.push(lhs);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            (Some(lhs), Some(rhs)) => {
 | 
				
			||||||
 | 
					                before.push(lhs);
 | 
				
			||||||
 | 
					                after.push(rhs);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            (None, Some(rhs)) => {
 | 
				
			||||||
 | 
					                after.push(rhs);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            (None, None) => {}
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        n -= r.len();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    (before, after)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn apply_snap_runs(
 | 
				
			||||||
 | 
					    origin: &Vec<Run>,
 | 
				
			||||||
 | 
					    snap: &Vec<SnapRun>,
 | 
				
			||||||
 | 
					    allocator: &mut Allocator,
 | 
				
			||||||
 | 
					) -> Result<Vec<Run>> {
 | 
				
			||||||
 | 
					    let mut origin = origin.clone();
 | 
				
			||||||
 | 
					    let mut runs = Vec::new();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for SnapRun(st, slen) in snap {
 | 
				
			||||||
 | 
					        let (os, rest) = split_runs(*slen, &origin);
 | 
				
			||||||
 | 
					        match st {
 | 
				
			||||||
 | 
					            SnapRunType::Same => {
 | 
				
			||||||
 | 
					                for o in os {
 | 
				
			||||||
 | 
					                    runs.push(o);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            SnapRunType::Diff => {
 | 
				
			||||||
 | 
					                for data in allocator.alloc(*slen)? {
 | 
				
			||||||
 | 
					                    runs.push(Run::Mapped {
 | 
				
			||||||
 | 
					                        data_begin: data.start,
 | 
				
			||||||
 | 
					                        len: data.end - data.start,
 | 
				
			||||||
 | 
					                    });
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            SnapRunType::Hole => {
 | 
				
			||||||
 | 
					                runs.push(Run::UnMapped { len: *slen });
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        origin = rest;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Ok(runs)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Snapshots share mappings, not neccessarily the entire ranges.
 | 
					// Snapshots share mappings, not neccessarily the entire ranges.
 | 
				
			||||||
struct SnapS {
 | 
					struct SnapS {
 | 
				
			||||||
    len: u64,
 | 
					    len: u64,
 | 
				
			||||||
@@ -633,7 +910,14 @@ impl SnapS {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
impl Scenario for SnapS {
 | 
					impl Scenario for SnapS {
 | 
				
			||||||
    fn generate_xml(&mut self, v: &mut dyn xml::MetadataVisitor) -> Result<()> {
 | 
					    fn generate_xml(&mut self, v: &mut dyn xml::MetadataVisitor) -> Result<()> {
 | 
				
			||||||
        let origin = mk_runs(0, self.len, 8..64);
 | 
					        let mut allocator = Allocator::new_shuffled(self.old_nr_data_blocks, 64..512);
 | 
				
			||||||
 | 
					        let origin = mk_origin(0, self.len, &mut allocator)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        v.superblock_b(&common_sb(self.old_nr_data_blocks))?;
 | 
				
			||||||
 | 
					        origin.emit(v)?;
 | 
				
			||||||
 | 
					        v.superblock_e()?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Ok(())
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn get_new_nr_blocks(&self) -> u64 {
 | 
					    fn get_new_nr_blocks(&self) -> u64 {
 | 
				
			||||||
@@ -646,5 +930,5 @@ fn shrink_identical_snap() -> Result<()> {
 | 
				
			|||||||
    let mut s = SnapS::new(1024, 1, 0);
 | 
					    let mut s = SnapS::new(1024, 1, 0);
 | 
				
			||||||
    test_shrink(&mut s)
 | 
					    test_shrink(&mut s)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
*/
 | 
					
 | 
				
			||||||
//------------------------------------
 | 
					//------------------------------------
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user