zebra-state: Add support for temporary sled databases (#939)

* Test config with persistent sled database
* Test ephemeral config
* Add misconfigured ephemeral test
This commit is contained in:
Ramana Venkata 2020-08-31 14:02:55 +05:30 committed by GitHub
parent d25cc20319
commit ad0001f7f7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 140 additions and 35 deletions

View File

@ -123,33 +123,35 @@ where
} }
#[tokio::test] #[tokio::test]
async fn batch_flushes_on_max_items() { async fn batch_flushes_on_max_items() -> Result<(), Report> {
use tokio::time::timeout; use tokio::time::timeout;
zebra_test::init(); zebra_test::init();
// Use a very long max_latency and a short timeout to check that // Use a very long max_latency and a short timeout to check that
// flushing is happening based on hitting max_items. // flushing is happening based on hitting max_items.
let verifier = Batch::new(Ed25519Verifier::new(), 10, Duration::from_secs(1000)); let verifier = Batch::new(Ed25519Verifier::new(), 10, Duration::from_secs(1000));
assert!(
timeout(Duration::from_secs(1), sign_and_verify(verifier, 100, None)) timeout(Duration::from_secs(1), sign_and_verify(verifier, 100, None))
.await .await
.is_ok() .map_err(|e| eyre!(e))?
); .map_err(|e| eyre!(e))?;
Ok(())
} }
#[tokio::test] #[tokio::test]
async fn batch_flushes_on_max_latency() { async fn batch_flushes_on_max_latency() -> Result<(), Report> {
use tokio::time::timeout; use tokio::time::timeout;
zebra_test::init(); zebra_test::init();
// Use a very high max_items and a short timeout to check that // Use a very high max_items and a short timeout to check that
// flushing is happening based on hitting max_latency. // flushing is happening based on hitting max_latency.
let verifier = Batch::new(Ed25519Verifier::new(), 100, Duration::from_millis(500)); let verifier = Batch::new(Ed25519Verifier::new(), 100, Duration::from_millis(500));
assert!(
timeout(Duration::from_secs(1), sign_and_verify(verifier, 10, None)) timeout(Duration::from_secs(1), sign_and_verify(verifier, 10, None))
.await .await
.is_ok() .map_err(|e| eyre!(e))?
); .map_err(|e| eyre!(e))?;
Ok(())
} }
#[tokio::test] #[tokio::test]

View File

@ -69,6 +69,13 @@ pub struct Config {
/// The maximum number of bytes to use caching data in memory. /// The maximum number of bytes to use caching data in memory.
pub memory_cache_bytes: u64, pub memory_cache_bytes: u64,
/// Whether to use an ephemeral database.
///
/// Ephemeral databases are stored in memory on Linux, and in a temporary directory on other OSes.
///
/// Set to `false` by default. If this is set to `true`, [`cache_dir`] is ignored.
pub ephemeral: bool,
} }
impl Config { impl Config {
@ -79,12 +86,16 @@ impl Config {
Network::Mainnet => "mainnet", Network::Mainnet => "mainnet",
Network::Testnet => "testnet", Network::Testnet => "testnet",
}; };
let path = self.cache_dir.join(net_dir).join("state");
sled::Config::default() let config = sled::Config::default()
.path(path)
.cache_capacity(self.memory_cache_bytes) .cache_capacity(self.memory_cache_bytes)
.mode(sled::Mode::LowSpace) .mode(sled::Mode::LowSpace);
if self.ephemeral {
config.temporary(self.ephemeral)
} else {
let path = self.cache_dir.join(net_dir).join("state");
config.path(path)
}
} }
} }
@ -96,6 +107,7 @@ impl Default for Config {
Self { Self {
cache_dir, cache_dir,
memory_cache_bytes: 512 * 1024 * 1024, memory_cache_bytes: 512 * 1024 * 1024,
ephemeral: false,
} }
} }
} }

View File

@ -11,15 +11,25 @@ use tempdir::TempDir;
use zebra_test::prelude::*; use zebra_test::prelude::*;
use zebrad::config::ZebradConfig; use zebrad::config::ZebradConfig;
pub fn tempdir(create_config: bool) -> Result<(PathBuf, impl Drop)> { #[derive(PartialEq)]
enum ConfigMode {
NoConfig,
Ephemeral,
Persistent,
}
fn tempdir(config_mode: ConfigMode) -> Result<(PathBuf, impl Drop)> {
let dir = TempDir::new("zebrad_tests")?; let dir = TempDir::new("zebrad_tests")?;
if create_config { if config_mode != ConfigMode::NoConfig {
let mut config = ZebradConfig::default();
if config_mode == ConfigMode::Ephemeral {
config.state.ephemeral = true;
} else {
let cache_dir = dir.path().join("state"); let cache_dir = dir.path().join("state");
fs::create_dir(&cache_dir)?; fs::create_dir(&cache_dir)?;
let mut config = ZebradConfig::default();
config.state.cache_dir = cache_dir; config.state.cache_dir = cache_dir;
}
config.state.memory_cache_bytes = 256000000; config.state.memory_cache_bytes = 256000000;
config.network.listen_addr = "127.0.0.1:0".parse()?; config.network.listen_addr = "127.0.0.1:0".parse()?;
@ -44,7 +54,7 @@ pub fn get_child(args: &[&str], tempdir: &PathBuf) -> Result<TestChild> {
#[test] #[test]
fn generate_no_args() -> Result<()> { fn generate_no_args() -> Result<()> {
zebra_test::init(); zebra_test::init();
let (tempdir, _guard) = tempdir(true)?; let (tempdir, _guard) = tempdir(ConfigMode::Ephemeral)?;
let child = get_child(&["generate"], &tempdir)?; let child = get_child(&["generate"], &tempdir)?;
let output = child.wait_with_output()?; let output = child.wait_with_output()?;
@ -59,7 +69,7 @@ fn generate_no_args() -> Result<()> {
#[test] #[test]
fn generate_args() -> Result<()> { fn generate_args() -> Result<()> {
zebra_test::init(); zebra_test::init();
let (tempdir, _guard) = tempdir(false)?; let (tempdir, _guard) = tempdir(ConfigMode::NoConfig)?;
// unexpected free argument `argument` // unexpected free argument `argument`
let child = get_child(&["generate", "argument"], &tempdir)?; let child = get_child(&["generate", "argument"], &tempdir)?;
@ -101,7 +111,7 @@ fn generate_args() -> Result<()> {
#[test] #[test]
fn help_no_args() -> Result<()> { fn help_no_args() -> Result<()> {
zebra_test::init(); zebra_test::init();
let (tempdir, _guard) = tempdir(true)?; let (tempdir, _guard) = tempdir(ConfigMode::Ephemeral)?;
let child = get_child(&["help"], &tempdir)?; let child = get_child(&["help"], &tempdir)?;
let output = child.wait_with_output()?; let output = child.wait_with_output()?;
@ -119,7 +129,7 @@ fn help_no_args() -> Result<()> {
#[test] #[test]
fn help_args() -> Result<()> { fn help_args() -> Result<()> {
zebra_test::init(); zebra_test::init();
let (tempdir, _guard) = tempdir(true)?; let (tempdir, _guard) = tempdir(ConfigMode::Ephemeral)?;
// The subcommand "argument" wasn't recognized. // The subcommand "argument" wasn't recognized.
let child = get_child(&["help", "argument"], &tempdir)?; let child = get_child(&["help", "argument"], &tempdir)?;
@ -137,7 +147,7 @@ fn help_args() -> Result<()> {
#[test] #[test]
fn revhex_args() -> Result<()> { fn revhex_args() -> Result<()> {
zebra_test::init(); zebra_test::init();
let (tempdir, _guard) = tempdir(true)?; let (tempdir, _guard) = tempdir(ConfigMode::Ephemeral)?;
// Valid // Valid
let child = get_child(&["revhex", "33eeff55"], &tempdir)?; let child = get_child(&["revhex", "33eeff55"], &tempdir)?;
@ -152,7 +162,7 @@ fn revhex_args() -> Result<()> {
#[test] #[test]
fn seed_no_args() -> Result<()> { fn seed_no_args() -> Result<()> {
zebra_test::init(); zebra_test::init();
let (tempdir, _guard) = tempdir(true)?; let (tempdir, _guard) = tempdir(ConfigMode::Ephemeral)?;
let mut child = get_child(&["-v", "seed"], &tempdir)?; let mut child = get_child(&["-v", "seed"], &tempdir)?;
@ -174,7 +184,7 @@ fn seed_no_args() -> Result<()> {
#[test] #[test]
fn seed_args() -> Result<()> { fn seed_args() -> Result<()> {
zebra_test::init(); zebra_test::init();
let (tempdir, _guard) = tempdir(true)?; let (tempdir, _guard) = tempdir(ConfigMode::Ephemeral)?;
// unexpected free argument `argument` // unexpected free argument `argument`
let child = get_child(&["seed", "argument"], &tempdir)?; let child = get_child(&["seed", "argument"], &tempdir)?;
@ -197,7 +207,8 @@ fn seed_args() -> Result<()> {
#[test] #[test]
fn start_no_args() -> Result<()> { fn start_no_args() -> Result<()> {
zebra_test::init(); zebra_test::init();
let (tempdir, _guard) = tempdir(true)?; // start caches state, so run one of the start tests with persistent state
let (tempdir, _guard) = tempdir(ConfigMode::Persistent)?;
let mut child = get_child(&["-v", "start"], &tempdir)?; let mut child = get_child(&["-v", "start"], &tempdir)?;
@ -221,7 +232,7 @@ fn start_no_args() -> Result<()> {
#[test] #[test]
fn start_args() -> Result<()> { fn start_args() -> Result<()> {
zebra_test::init(); zebra_test::init();
let (tempdir, _guard) = tempdir(true)?; let (tempdir, _guard) = tempdir(ConfigMode::Ephemeral)?;
// Any free argument is valid // Any free argument is valid
let mut child = get_child(&["start", "argument"], &tempdir)?; let mut child = get_child(&["start", "argument"], &tempdir)?;
@ -243,10 +254,90 @@ fn start_args() -> Result<()> {
Ok(()) Ok(())
} }
#[test]
fn persistent_mode() -> Result<()> {
zebra_test::init();
let (tempdir, _guard) = tempdir(ConfigMode::Persistent)?;
let mut child = get_child(&["-v", "start"], &tempdir)?;
// Run the program and kill it at 1 second
std::thread::sleep(Duration::from_secs(1));
child.kill()?;
let output = child.wait_with_output()?;
// Make sure the command was killed
assert!(output.was_killed());
// Check that we have persistent sled database
let cache_dir = tempdir.join("state");
assert!(cache_dir.read_dir()?.count() > 0);
Ok(())
}
#[test]
fn ephemeral_mode() -> Result<()> {
zebra_test::init();
let (tempdir, _guard) = tempdir(ConfigMode::Ephemeral)?;
// Any free argument is valid
let mut child = get_child(&["start", "argument"], &tempdir)?;
// Run the program and kill it at 1 second
std::thread::sleep(Duration::from_secs(1));
child.kill()?;
let output = child.wait_with_output()?;
// Make sure the command was killed
assert!(output.was_killed());
let cache_dir = tempdir.join("state");
assert!(!cache_dir.exists());
Ok(())
}
#[test]
fn misconfigured_ephemeral_mode() -> Result<()> {
zebra_test::init();
let dir = TempDir::new("zebrad_tests")?;
let cache_dir = dir.path().join("state");
// Write a configuration that has both cache_dir and ephemeral options set
let mut config = ZebradConfig::default();
config.state.ephemeral = true;
// Although cache_dir has a default value, we set it a new temp directory
// to test that it is empty later.
fs::create_dir(&cache_dir)?;
config.state.cache_dir = cache_dir.clone();
config.state.memory_cache_bytes = 256000000;
config.network.listen_addr = "127.0.0.1:0".parse()?;
fs::File::create(dir.path().join("zebrad.toml"))?
.write_all(toml::to_string(&config)?.as_bytes())?;
let tempdir = dir.path().to_path_buf();
// Any free argument is valid
let mut child = get_child(&["start", "argument"], &tempdir)?;
// Run the program and kill it at 1 second
std::thread::sleep(Duration::from_secs(1));
child.kill()?;
let output = child.wait_with_output()?;
// Make sure the command was killed
assert!(output.was_killed());
// Check that ephemeral takes precedence over cache_dir
assert_eq!(cache_dir.read_dir()?.count(), 0);
Ok(())
}
#[test] #[test]
fn app_no_args() -> Result<()> { fn app_no_args() -> Result<()> {
zebra_test::init(); zebra_test::init();
let (tempdir, _guard) = tempdir(true)?; let (tempdir, _guard) = tempdir(ConfigMode::Ephemeral)?;
let child = get_child(&[], &tempdir)?; let child = get_child(&[], &tempdir)?;
let output = child.wait_with_output()?; let output = child.wait_with_output()?;
@ -260,7 +351,7 @@ fn app_no_args() -> Result<()> {
#[test] #[test]
fn version_no_args() -> Result<()> { fn version_no_args() -> Result<()> {
zebra_test::init(); zebra_test::init();
let (tempdir, _guard) = tempdir(true)?; let (tempdir, _guard) = tempdir(ConfigMode::Ephemeral)?;
let child = get_child(&["version"], &tempdir)?; let child = get_child(&["version"], &tempdir)?;
let output = child.wait_with_output()?; let output = child.wait_with_output()?;
@ -274,7 +365,7 @@ fn version_no_args() -> Result<()> {
#[test] #[test]
fn version_args() -> Result<()> { fn version_args() -> Result<()> {
zebra_test::init(); zebra_test::init();
let (tempdir, _guard) = tempdir(true)?; let (tempdir, _guard) = tempdir(ConfigMode::Ephemeral)?;
// unexpected free argument `argument` // unexpected free argument `argument`
let child = get_child(&["version", "argument"], &tempdir)?; let child = get_child(&["version", "argument"], &tempdir)?;
@ -302,7 +393,7 @@ fn valid_generated_config_test() -> Result<()> {
fn valid_generated_config(command: &str, expected_output: &str) -> Result<()> { fn valid_generated_config(command: &str, expected_output: &str) -> Result<()> {
zebra_test::init(); zebra_test::init();
let (tempdir, _guard) = tempdir(false)?; let (tempdir, _guard) = tempdir(ConfigMode::NoConfig)?;
// Add a config file name to tempdir path // Add a config file name to tempdir path
let mut generated_config_path = tempdir.clone(); let mut generated_config_path = tempdir.clone();