#![allow(clippy::uninlined_format_args, clippy::unused_unit)]
use ballista_core::error::BallistaError;
use crate::executor_process::ExecutorProcessConfig;
use crate::metrics::ExecutorMetricCollectionPolicy;
fn parse_memory_pool_size(s: &str) -> Result<u64, String> {
s.parse::<bytesize::ByteSize>()
.map(|b| b.as_u64())
.map_err(|e| format!("invalid byte size '{s}': {e}"))
}
#[cfg(feature = "build-binary")]
#[derive(clap::Parser, Debug)]
#[command(version, about, long_about = None)]
pub struct Config {
#[arg(long, default_value_t = String::from("localhost"), help = "Scheduler host.")]
pub scheduler_host: String,
#[arg(long, default_value_t = 50050, help = "Scheduler port.")]
pub scheduler_port: u16,
#[arg(long, default_value_t = String::from("0.0.0.0"), help = "Local IP address to bind to.")]
pub bind_host: String,
#[arg(
long,
help = "Host name or IP address to register with scheduler so that other executors can connect to this executor. If none is provided, the scheduler will use the connecting IP address to communicate with the executor."
)]
pub external_host: Option<String>,
#[arg(short = 'p', long, default_value_t = 50051, help = "bind port")]
pub bind_port: u16,
#[arg(long, default_value_t = 50052, help = "Grpc service bind port.")]
pub bind_grpc_port: u16,
#[arg(
long,
default_value_t = 0,
help = "How long to try connecting to scheduler before failing. Set to zero to fail after first attempt."
)]
pub scheduler_connect_timeout_seconds: u16,
#[arg(long, help = "Directory for temporary IPC files")]
pub work_dir: Option<String>,
#[arg(
short = 'c',
long,
default_value_t = 0,
help = "Max concurrent tasks (defaults to all available cores if left as zero)."
)]
pub concurrent_tasks: usize,
#[arg(short = 's', long, default_value_t = ballista_core::config::TaskSchedulingPolicy::default(), help = "The task scheduling policy used by scheduler. Configuration must match with scheduler configured policy.")]
pub task_scheduling_policy: ballista_core::config::TaskSchedulingPolicy,
#[arg(
long,
default_value_t = 0,
help = "Controls the interval in seconds, which the worker cleans up old job dirs on the local machine. 0 means the clean up is disabled."
)]
pub job_data_clean_up_interval_seconds: u64,
#[arg(
long,
default_value_t = 604800,
help = "The number of seconds to retain job directories on each worker 604800 (7 days, 7 * 24 * 3600), In other words, after job done, how long the resulting data is retained."
)]
pub job_data_ttl_seconds: u64,
#[arg(
long,
help = "Log dir: a path to save log. This will create a new storage directory at the specified path if it does not already exist."
)]
pub log_dir: Option<String>,
#[arg(
long,
default_value_t = true,
help = "Enable print thread ids and names in log file."
)]
pub print_thread_info: bool,
#[arg(
long,
default_value_t = String::from("INFO,datafusion=INFO"),
help = "special log level for sub mod. link: https://docs.rs/env_logger/latest/env_logger/#enabling-logging. For example we want whole level is INFO but datafusion mode is DEBUG."
)]
pub log_level_setting: String,
#[arg(
long,
default_value_t = ballista_core::config::LogRotationPolicy::Daily,
help = "Tracing log rotation policy."
)]
pub log_rotation_policy: ballista_core::config::LogRotationPolicy,
#[arg(
long,
default_value_t = 16777216,
help = "The maximum size of a decoded message at the grpc server side."
)]
pub grpc_server_max_decoding_message_size: u32,
#[arg(
long,
default_value_t = 16777216,
help = "The maximum size of an encoded message at the grpc server side."
)]
pub grpc_server_max_encoding_message_size: u32,
#[arg(
long,
default_value_t = 60,
help = "The heartbeat interval in seconds to the scheduler for push-based task scheduling."
)]
pub executor_heartbeat_interval_seconds: u64,
#[arg(
short = 'm',
long = "metrics",
default_value_t = ExecutorMetricCollectionPolicy::default(),
help = "Metric collection policy of this executor instance"
)]
pub metric_collection_policy: ExecutorMetricCollectionPolicy,
#[arg(
long,
value_parser = parse_memory_pool_size,
help = "Optional total executor memory budget (e.g. \"8GB\", \"512MiB\"). Each concurrent task receives an equal share."
)]
pub memory_pool_size: Option<u64>,
#[arg(
long,
default_value_t = 16,
help = "Max number of sessions whose shared runtime state (object-store clients, Parquet footer cache) is retained on the executor (LRU). 0 disables caching."
)]
pub session_runtime_cache_capacity: usize,
#[arg(
long,
default_value_t = 0,
help = "Number of seconds established client connection should be cached if not used (0 means no cache, connection will be disposed)."
)]
pub client_ttl: u64,
}
impl TryFrom<Config> for ExecutorProcessConfig {
type Error = BallistaError;
fn try_from(opt: Config) -> Result<Self, Self::Error> {
Ok(ExecutorProcessConfig {
special_mod_log_level: opt.log_level_setting,
external_host: opt.external_host,
bind_host: opt.bind_host,
port: opt.bind_port,
grpc_port: opt.bind_grpc_port,
scheduler_host: opt.scheduler_host,
scheduler_port: opt.scheduler_port,
scheduler_connect_timeout_seconds: opt.scheduler_connect_timeout_seconds,
concurrent_tasks: opt.concurrent_tasks,
task_scheduling_policy: opt.task_scheduling_policy,
work_dir: opt.work_dir,
log_dir: opt.log_dir,
log_rotation_policy: opt.log_rotation_policy,
print_thread_info: opt.print_thread_info,
job_data_ttl_seconds: opt.job_data_ttl_seconds,
job_data_clean_up_interval_seconds: opt.job_data_clean_up_interval_seconds,
grpc_max_decoding_message_size: opt.grpc_server_max_decoding_message_size,
grpc_max_encoding_message_size: opt.grpc_server_max_encoding_message_size,
grpc_server_config: ballista_core::utils::GrpcServerConfig::default(),
executor_heartbeat_interval_seconds: opt.executor_heartbeat_interval_seconds,
metric_collection_policy: opt.metric_collection_policy,
memory_pool_size: opt.memory_pool_size,
session_runtime_cache_capacity: opt.session_runtime_cache_capacity,
override_execution_engine: None,
override_function_registry: None,
override_config_producer: None,
override_runtime_producer: None,
override_logical_codec: None,
override_physical_codec: None,
override_arrow_flight_service: None,
override_create_grpc_client_endpoint: None,
client_ttl: opt.client_ttl,
})
}
}
#[cfg(test)]
mod tests {
use super::parse_memory_pool_size;
#[test]
fn parse_decimal_suffix() {
assert_eq!(parse_memory_pool_size("8GB").unwrap(), 8_000_000_000);
assert_eq!(parse_memory_pool_size("1KB").unwrap(), 1_000);
}
#[test]
fn parse_binary_suffix() {
assert_eq!(parse_memory_pool_size("512MiB").unwrap(), 512 * 1024 * 1024);
assert_eq!(parse_memory_pool_size("1KiB").unwrap(), 1024);
}
#[test]
fn parse_plain_integer_is_bytes() {
assert_eq!(parse_memory_pool_size("1024").unwrap(), 1024);
}
#[test]
fn parse_rejects_invalid() {
assert!(parse_memory_pool_size("banana").is_err());
assert!(parse_memory_pool_size("").is_err());
}
}