Refactor cache configuration

This commit is contained in:
Artur Jamro
2019-09-06 15:09:05 -07:00
committed by Dan Gohman
parent c3a519d3a1
commit 1c22211d57
14 changed files with 747 additions and 592 deletions

View File

@@ -50,7 +50,7 @@ use std::str;
use std::str::FromStr;
use target_lexicon::Triple;
use wasmtime_debug::{emit_debugsections, read_debuginfo};
use wasmtime_environ::cache_config;
use wasmtime_environ::cache_init;
use wasmtime_environ::{
Compiler, Cranelift, ModuleEnvironment, ModuleVmctxInfo, Tunables, VMOffsets,
};
@@ -123,7 +123,7 @@ fn main() {
Some(prefix)
};
let errors = cache_config::init(
let errors = cache_init(
args.flag_cache || args.flag_cache_config_file.is_some(),
args.flag_cache_config_file.as_ref(),
args.flag_create_cache_config,

View File

@@ -45,7 +45,7 @@ use std::process::exit;
use wabt;
use wasi_common::preopen_dir;
use wasmtime_api::{Config, Engine, HostRef, Instance, Module, Store};
use wasmtime_environ::cache_config;
use wasmtime_environ::cache_init;
use wasmtime_interface_types::ModuleData;
use wasmtime_jit::Features;
use wasmtime_wasi::instantiate_wasi;
@@ -222,7 +222,7 @@ fn rmain() -> Result<(), Error> {
Some(prefix)
};
let errors = cache_config::init(
let errors = cache_init(
args.flag_cache || args.flag_cache_config_file.is_some(),
args.flag_cache_config_file.as_ref(),
args.flag_create_cache_config,

View File

@@ -33,7 +33,7 @@ use pretty_env_logger;
use serde::Deserialize;
use std::path::Path;
use std::process;
use wasmtime_environ::cache_config;
use wasmtime_environ::cache_init;
use wasmtime_jit::{Compiler, Features};
use wasmtime_wast::WastContext;
@@ -89,7 +89,7 @@ fn main() {
Some(prefix)
};
let errors = cache_config::init(
let errors = cache_init(
args.flag_cache || args.flag_cache_config_file.is_some(),
args.flag_cache_config_file.as_ref(),
args.flag_create_cache_config,

View File

@@ -15,10 +15,13 @@ use std::io::Write;
use std::path::{Path, PathBuf};
use std::string::{String, ToString};
pub mod config;
use config as cache_config; // so we have namespaced methods
mod config;
mod worker;
pub use config::init;
use config::{cache_config, CacheConfig};
use worker::worker;
lazy_static! {
static ref SELF_MTIME: String = {
std::env::current_exe()
@@ -45,8 +48,9 @@ lazy_static! {
};
}
pub struct ModuleCacheEntry {
pub struct ModuleCacheEntry<'config> {
mod_cache_path: Option<PathBuf>,
cache_config: &'config CacheConfig,
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
@@ -68,7 +72,7 @@ type ModuleCacheDataTupleType = (
struct Sha256Hasher(Sha256);
impl ModuleCacheEntry {
impl<'config> ModuleCacheEntry<'config> {
pub fn new<'data>(
module: &Module,
function_body_inputs: &PrimaryMap<DefinedFuncIndex, FunctionBodyData<'data>>,
@@ -76,7 +80,25 @@ impl ModuleCacheEntry {
compiler_name: &str,
generate_debug_info: bool,
) -> Self {
let mod_cache_path = if cache_config::enabled() {
Self::new_with_config(
module,
function_body_inputs,
isa,
compiler_name,
generate_debug_info,
cache_config(),
)
}
fn new_with_config<'data>(
module: &Module,
function_body_inputs: &PrimaryMap<DefinedFuncIndex, FunctionBodyData<'data>>,
isa: &dyn isa::TargetIsa,
compiler_name: &str,
generate_debug_info: bool,
cache_config: &'config CacheConfig,
) -> Self {
let mod_cache_path = if cache_config.enabled() {
let hash = Sha256Hasher::digest(module, function_body_inputs);
let compiler_dir = if cfg!(debug_assertions) {
format!(
@@ -98,7 +120,8 @@ impl ModuleCacheEntry {
mod_dbg = if generate_debug_info { ".d" } else { "" },
);
Some(
cache_config::directory()
cache_config
.directory()
.join(isa.triple().to_string())
.join(compiler_dir)
.join(mod_filename),
@@ -107,7 +130,10 @@ impl ModuleCacheEntry {
None
};
Self { mod_cache_path }
Self {
mod_cache_path,
cache_config,
}
}
pub fn get_data(&self) -> Option<ModuleCacheData> {
@@ -121,14 +147,14 @@ impl ModuleCacheEntry {
.map_err(|err| warn!("Failed to deserialize cached code: {}", err))
.ok()?;
worker::on_cache_get_async(path); // call on success
worker().on_cache_get_async(path); // call on success
Some(ret)
}
pub fn update_data(&self, data: &ModuleCacheData) {
if self.update_data_impl(data).is_some() {
let path = self.mod_cache_path.as_ref().unwrap();
worker::on_cache_update_async(path); // call on success
worker().on_cache_update_async(path); // call on success
}
}
@@ -140,7 +166,7 @@ impl ModuleCacheEntry {
.ok()?;
let compressed_data = zstd::encode_all(
&serialized_data[..],
cache_config::baseline_compression_level(),
self.cache_config.baseline_compression_level(),
)
.map_err(|err| warn!("Failed to compress cached code: {}", err))
.ok()?;

View File

@@ -22,46 +22,53 @@ struct Config {
cache: CacheConfig,
}
// todo: markdown documention of these options
// todo: markdown documention of these options (name, format, default, explanation)
// todo: don't flush default values (create config from simple template + url to docs)
// todo: more user-friendly cache config creation
#[derive(Serialize, Deserialize, Debug)]
struct CacheConfig {
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct CacheConfig {
#[serde(skip)]
pub errors: Vec<String>,
errors: Vec<String>,
pub enabled: bool,
pub directory: Option<PathBuf>,
enabled: bool,
directory: Option<PathBuf>,
#[serde(rename = "worker-event-queue-size")]
pub worker_event_queue_size: Option<usize>,
worker_event_queue_size: Option<usize>,
#[serde(rename = "baseline-compression-level")]
pub baseline_compression_level: Option<i32>,
baseline_compression_level: Option<i32>,
#[serde(rename = "optimized-compression-level")]
pub optimized_compression_level: Option<i32>,
optimized_compression_level: Option<i32>,
#[serde(rename = "optimized-compression-usage-counter-threshold")]
pub optimized_compression_usage_counter_threshold: Option<u64>,
optimized_compression_usage_counter_threshold: Option<u64>,
#[serde(
default,
rename = "cleanup-interval-in-seconds",
serialize_with = "serialize_duration",
deserialize_with = "deserialize_duration"
)] // todo unit?
pub cleanup_interval: Option<Duration>,
cleanup_interval: Option<Duration>,
#[serde(
default,
rename = "optimizing-compression-task-timeout-in-seconds",
serialize_with = "serialize_duration",
deserialize_with = "deserialize_duration"
)] // todo unit?
pub optimizing_compression_task_timeout: Option<Duration>,
optimizing_compression_task_timeout: Option<Duration>,
#[serde(
default,
rename = "allowed-clock-drift-for-locks-from-future",
serialize_with = "serialize_duration",
deserialize_with = "deserialize_duration"
)] // todo unit?
allowed_clock_drift_for_locks_from_future: Option<Duration>,
#[serde(rename = "files-count-soft-limit")]
pub files_count_soft_limit: Option<u64>,
files_count_soft_limit: Option<u64>,
#[serde(rename = "files-total-size-soft-limit")]
pub files_total_size_soft_limit: Option<u64>, // todo unit?
files_total_size_soft_limit: Option<u64>, // todo unit?
#[serde(rename = "files-count-limit-percent-if-deleting")]
pub files_count_limit_percent_if_deleting: Option<u8>, // todo format: integer + %
files_count_limit_percent_if_deleting: Option<u8>, // todo format: integer + %
#[serde(rename = "files-total-size-limit-percent-if-deleting")]
pub files_total_size_limit_percent_if_deleting: Option<u8>,
files_total_size_limit_percent_if_deleting: Option<u8>,
}
// toml-rs fails to serialize Duration ("values must be emitted before tables")
@@ -84,53 +91,14 @@ where
static CONFIG: Once<CacheConfig> = Once::new();
static INIT_CALLED: AtomicBool = AtomicBool::new(false);
/// Returns true if and only if the cache is enabled.
pub fn enabled() -> bool {
// Not everyone knows about the cache system, i.e. the tests,
// so the default is cache disabled.
CONFIG
.call_once(|| CacheConfig::new_cache_disabled())
.enabled
}
/// Returns path to the cache directory.
/// Returns cache configuration.
///
/// Panics if the cache is disabled.
pub fn directory() -> &'static PathBuf {
&CONFIG
.r#try()
.expect("Cache system must be initialized")
.directory
.as_ref()
.expect("All cache system settings must be validated or defaulted")
/// If system has not been initialized, it disables it.
/// You mustn't call init() after it.
pub fn cache_config() -> &'static CacheConfig {
CONFIG.call_once(|| CacheConfig::new_cache_disabled())
}
macro_rules! generate_setting_getter {
($setting:ident: $setting_type:ty) => {
/// Returns `$setting`.
///
/// Panics if the cache is disabled.
pub fn $setting() -> $setting_type {
CONFIG
.r#try()
.expect("Cache system must be initialized")
.$setting
.expect("All cache system settings must be validated or defaulted")
}
};
}
generate_setting_getter!(worker_event_queue_size: usize);
generate_setting_getter!(baseline_compression_level: i32);
generate_setting_getter!(optimized_compression_level: i32);
generate_setting_getter!(optimized_compression_usage_counter_threshold: u64);
generate_setting_getter!(cleanup_interval: Duration);
generate_setting_getter!(optimizing_compression_task_timeout: Duration);
generate_setting_getter!(files_count_soft_limit: u64);
generate_setting_getter!(files_total_size_soft_limit: u64);
generate_setting_getter!(files_count_limit_percent_if_deleting: u8);
generate_setting_getter!(files_total_size_limit_percent_if_deleting: u8);
/// Initializes the cache system. Should be called exactly once,
/// and before using the cache system. Otherwise it can panic.
/// Returns list of errors. If empty, initialization succeeded.
@@ -178,12 +146,53 @@ const DEFAULT_OPTIMIZED_COMPRESSION_LEVEL: i32 = 20;
const DEFAULT_OPTIMIZED_COMPRESSION_USAGE_COUNTER_THRESHOLD: u64 = 0x100;
const DEFAULT_CLEANUP_INTERVAL: Duration = Duration::from_secs(60 * 60);
const DEFAULT_OPTIMIZING_COMPRESSION_TASK_TIMEOUT: Duration = Duration::from_secs(30 * 60);
const DEFAULT_ALLOWED_CLOCK_DRIFT_FOR_LOCKS_FROM_FUTURE: Duration =
Duration::from_secs(60 * 60 * 24);
const DEFAULT_FILES_COUNT_SOFT_LIMIT: u64 = 0x10_000;
const DEFAULT_FILES_TOTAL_SIZE_SOFT_LIMIT: u64 = 1024 * 1024 * 512;
const DEFAULT_FILES_COUNT_LIMIT_PERCENT_IF_DELETING: u8 = 70;
const DEFAULT_FILES_TOTAL_SIZE_LIMIT_PERCENT_IF_DELETING: u8 = 70;
macro_rules! generate_setting_getter {
($setting:ident: $setting_type:ty) => {
/// Returns `$setting`.
///
/// Panics if the cache is disabled.
pub fn $setting(&self) -> $setting_type {
self
.$setting
.expect("All cache system settings must be validated or defaulted")
}
};
}
impl CacheConfig {
generate_setting_getter!(worker_event_queue_size: usize);
generate_setting_getter!(baseline_compression_level: i32);
generate_setting_getter!(optimized_compression_level: i32);
generate_setting_getter!(optimized_compression_usage_counter_threshold: u64);
generate_setting_getter!(cleanup_interval: Duration);
generate_setting_getter!(optimizing_compression_task_timeout: Duration);
generate_setting_getter!(allowed_clock_drift_for_locks_from_future: Duration);
generate_setting_getter!(files_count_soft_limit: u64);
generate_setting_getter!(files_total_size_soft_limit: u64);
generate_setting_getter!(files_count_limit_percent_if_deleting: u8);
generate_setting_getter!(files_total_size_limit_percent_if_deleting: u8);
/// Returns true if and only if the cache is enabled.
pub fn enabled(&self) -> bool {
self.enabled
}
/// Returns path to the cache directory.
///
/// Panics if the cache is disabled.
pub fn directory(&self) -> &PathBuf {
self.directory
.as_ref()
.expect("All cache system settings must be validated or defaulted")
}
pub fn new_cache_disabled() -> Self {
Self {
errors: Vec::new(),
@@ -195,6 +204,7 @@ impl CacheConfig {
optimized_compression_usage_counter_threshold: None,
cleanup_interval: None,
optimizing_compression_task_timeout: None,
allowed_clock_drift_for_locks_from_future: None,
files_count_soft_limit: None,
files_total_size_soft_limit: None,
files_count_limit_percent_if_deleting: None,
@@ -237,6 +247,7 @@ impl CacheConfig {
config.validate_optimized_compression_usage_counter_threshold_or_default();
config.validate_cleanup_interval_or_default();
config.validate_optimizing_compression_task_timeout_or_default();
config.validate_allowed_clock_drift_for_locks_from_future_or_default();
config.validate_files_count_soft_limit_or_default();
config.validate_files_total_size_soft_limit_or_default();
config.validate_files_count_limit_percent_if_deleting_or_default();
@@ -410,6 +421,13 @@ impl CacheConfig {
}
}
fn validate_allowed_clock_drift_for_locks_from_future_or_default(&mut self) {
if self.allowed_clock_drift_for_locks_from_future.is_none() {
self.allowed_clock_drift_for_locks_from_future =
Some(DEFAULT_ALLOWED_CLOCK_DRIFT_FOR_LOCKS_FROM_FUTURE);
}
}
fn validate_files_count_soft_limit_or_default(&mut self) {
if self.files_count_soft_limit.is_none() {
self.files_count_soft_limit = Some(DEFAULT_FILES_COUNT_SOFT_LIMIT);

View File

@@ -19,7 +19,7 @@ use tempfile;
// Since cache system is a global thing, each test needs to be run in seperate process.
// So, init() tests are run as integration tests.
// However, caching is a private thing, an implementation detail, and needs to be tested
// from the inside of the module. Thus we have one big test here.
// from the inside of the module.
#[test]
fn test_write_read_cache() {
@@ -40,16 +40,17 @@ fn test_write_read_cache() {
);
fs::write(&config_path, config_content).expect("Failed to write test config file");
let errors = cache_config::init(true, Some(&config_path), false, None);
let errors = init(true, Some(&config_path), false, None);
assert!(errors.is_empty());
assert!(cache_config::enabled());
let cache_config = cache_config();
assert!(cache_config.enabled());
// assumption: config init creates cache directory and returns canonicalized path
assert_eq!(
*cache_config::directory(),
*cache_config.directory(),
fs::canonicalize(cache_dir).unwrap()
);
assert_eq!(
cache_config::baseline_compression_level(),
cache_config.baseline_compression_level(),
baseline_compression_level
);
@@ -276,7 +277,7 @@ fn new_module_cache_data(rng: &mut impl Rng) -> ModuleCacheData {
))
}
impl ModuleCacheEntry {
impl ModuleCacheEntry<'_> {
pub fn mod_cache_path(&self) -> &Option<PathBuf> {
&self.mod_cache_path
}

File diff suppressed because it is too large Load Diff

View File

@@ -54,7 +54,7 @@ pub mod lightbeam;
pub use crate::address_map::{
FunctionAddressMap, InstructionAddressMap, ModuleAddressMap, ModuleVmctxInfo, ValueLabelsRanges,
};
pub use crate::cache::config as cache_config;
pub use crate::cache::init as cache_init;
pub use crate::compilation::{
Compilation, CompileError, Compiler, Relocation, RelocationTarget, Relocations,
};

View File

@@ -1,8 +1,8 @@
use wasmtime_environ::cache_config;
use wasmtime_environ::cache_init;
#[test]
fn test_cache_default_config_in_memory() {
let errors = cache_config::init::<&str>(true, None, false, None);
let errors = cache_init::<&str>(true, None, false, None);
assert!(
errors.is_empty(),
"This test loads config from the default location, if there's one. Make sure it's correct!"

View File

@@ -1,6 +1,6 @@
use std::fs;
use tempfile;
use wasmtime_environ::cache_config;
use wasmtime_environ::cache_init;
#[test]
#[should_panic]
@@ -20,7 +20,7 @@ fn test_cache_fail_calling_init_twice() {
);
fs::write(&config_path, config_content).expect("Failed to write test config file");
let errors = cache_config::init(true, Some(&config_path), false, None);
let errors = cache_init(true, Some(&config_path), false, None);
assert!(errors.is_empty());
let _errors = cache_config::init(true, Some(&config_path), false, None);
let _errors = cache_init(true, Some(&config_path), false, None);
}

View File

@@ -1,6 +1,6 @@
use std::fs;
use tempfile;
use wasmtime_environ::cache_config;
use wasmtime_environ::cache_init;
#[test]
fn test_cache_fail_invalid_config() {
@@ -18,6 +18,6 @@ fn test_cache_fail_invalid_config() {
);
fs::write(&config_path, config_content).expect("Failed to write test config file");
let errors = cache_config::init(true, Some(&config_path), false, None);
let errors = cache_init(true, Some(&config_path), false, None);
assert!(!errors.is_empty());
}

View File

@@ -1,10 +1,10 @@
use tempfile;
use wasmtime_environ::cache_config;
use wasmtime_environ::cache_init;
#[test]
fn test_cache_fail_invalid_path_to_config() {
let dir = tempfile::tempdir().expect("Can't create temporary directory");
let config_path = dir.path().join("cache-config.toml"); // doesn't exist
let errors = cache_config::init(true, Some(&config_path), false, None);
let errors = cache_init(true, Some(&config_path), false, None);
assert!(!errors.is_empty());
}

View File

@@ -1,15 +0,0 @@
// These tests doesn't call init(), so we can test a multiple certain things here
use wasmtime_environ::cache_config;
#[test]
#[should_panic]
fn test_cache_fail_usage_without_init_directory() {
let _ = cache_config::directory();
}
#[test]
#[should_panic]
fn test_cache_fail_usage_without_init_baseline_compression_level() {
let _ = cache_config::baseline_compression_level();
}

View File

@@ -1,12 +1,12 @@
use tempfile;
use wasmtime_environ::cache_config;
use wasmtime_environ::cache_init;
#[test]
fn test_cache_write_default_config() {
let dir = tempfile::tempdir().expect("Can't create temporary directory");
let config_path = dir.path().join("cache-config.toml");
let errors = cache_config::init(true, Some(&config_path), true, None);
let errors = cache_init(true, Some(&config_path), true, None);
assert!(errors.is_empty());
assert!(config_path.exists());
}