wal-rs 1.0.2

Write-ahead-log implementation
Documentation
use config::Config;
use mock::{random_bytes, Home};
use rand::{thread_rng, Rng};
use segment::Segment;
use std::collections::HashSet;
use std::fmt::Display;
use std::path::{Path, PathBuf};
use wal::WAL;

#[test]
fn test_open_reopen() {
    let per: usize = 100;
    let cfg = Config {
        entry_per_segment: per,
        check_crc32: false,
    };

    let testhome = Home::new("testdir");
    let entry_num: usize = 256;
    let dir = testhome.dir();
    let data = random_bytes(entry_num);
    let buf = data.as_slice();

    {
        let title = "init & write";
        let mut wal = WAL::open(&testhome.dir(), cfg.clone()).unwrap();

        for i in 0..buf.len() {
            wal.write(&buf[..i + 1]).unwrap();
        }

        assert_eq!(wal.segments.len(), 3, "{}", title);
        assert_eq!(wal.len(), entry_num, "{}", title);
        assert_segment_exists(&dir, &vec![0, 1, 2], title);
    }

    {
        let title = "read half in segment 0";
        let mut wal = WAL::open(&testhome.dir(), cfg.clone()).unwrap();

        let read_n = per / 2;
        let out = wal.read(read_n).unwrap();

        assert_eq!(out.len(), read_n, "{}", title);

        assert_eq!(wal.segments.len(), 3, "{}", title);
        assert_eq!(wal.len(), entry_num - read_n, "{}", title);
        assert_segment_exists(&dir, &vec![0, 1, 2], title);
    }

    {
        let title = "read whole segment 0";
        let mut wal = WAL::open(&testhome.dir(), cfg.clone()).unwrap();

        let read_n = per / 2;
        let out = wal.read(read_n).unwrap();

        assert_eq!(out.len(), read_n, "{}", title);

        assert_eq!(wal.segments.len(), 3, "{}", title);
        assert_eq!(wal.len(), entry_num - per, "{}", title);
        assert_segment_exists(&dir, &vec![0, 1, 2], title);
    }

    {
        let title = "read half in segment 1";
        let mut wal = WAL::open(&testhome.dir(), cfg.clone()).unwrap();

        let read_n = per / 2;
        let out = wal.read(read_n).unwrap();

        assert_eq!(out.len(), read_n, "{}", title);

        assert_eq!(wal.segments.len(), 2, "{}", title);
        assert_eq!(wal.len(), entry_num - per - read_n, "{}", title);
        assert_segment_exists(&dir, &vec![1, 2], title);
    }

    {
        let title = "read all";
        let mut wal = WAL::open(&testhome.dir(), cfg.clone()).unwrap();

        let read_n = entry_num - per / 2 * 3;
        let out = wal.read(entry_num * 2).unwrap();

        assert_eq!(out.len(), read_n, "{}", title);

        assert_eq!(wal.segments.len(), 1, "{}", title);
        assert_eq!(wal.len(), 0, "{}", title);
        assert_segment_exists(&dir, &vec![2], title);
    }
}

fn assert_segment_exists<T: Display>(dir: &PathBuf, seqs: &[u64], title: T) {
    for seq in seqs {
        let fname = Segment::filename(*seq);
        let f = Path::new(dir).join(&fname);
        assert!(f.exists() && f.is_file(), "{} {}", title, fname);
    }
}

#[test]
fn test_batch() {
    let per: usize = 10;
    let cfg = Config {
        entry_per_segment: per,
        check_crc32: false,
    };

    let testhome = Home::new("testdir");
    let entry_num: usize = 1024;
    let segment_num = (entry_num + per - 1) / per;
    let dir = testhome.dir();
    let data = random_bytes(entry_num);
    let buf = data.as_slice();
    let mut wal = WAL::open(&dir, cfg.clone()).unwrap();

    {
        let title = "batch write";

        let mut batch: Vec<&[u8]> = Vec::with_capacity(entry_num);

        for i in 0..buf.len() {
            batch.push(&buf[0..i + 1]);
        }

        wal.batch_write(&batch).unwrap();

        assert_eq!(wal.segments.len(), segment_num, "{}", title);
        assert_eq!(wal.len(), entry_num, "{}", title);
    }

    {
        let title = "batch read";
        let mut rng = thread_rng();
        let mut set = HashSet::new();
        let mut left = entry_num;

        while left > 0 {
            assert_eq!(wal.len(), left, "{}", title);

            let read = if left < per {
                left
            } else {
                rng.gen_range(per / 2, left)
            };

            left -= read;

            let out = wal.read(read).unwrap();
            assert_eq!(out.len(), read);
            assert_eq!(wal.len(), left, "{}", title);

            for one in out {
                assert!(set.insert(one.len()), "{}", title);
            }
        }

        assert_eq!(set.len(), entry_num);
    }
}