use std::{
collections::{BTreeMap, BTreeSet},
fmt::{self, Display},
path::PathBuf,
};
use ave_common::identity::{HashAlgorithm, KeyPairAlgorithm};
use ave_network::Config as NetworkConfig;
use serde::{Deserialize, Deserializer, Serialize};
use crate::{helpers::sink::TokenResponse, subject::sinkdata::SinkTypes};
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(default)]
#[serde(rename_all = "snake_case")]
pub struct Config {
pub keypair_algorithm: KeyPairAlgorithm,
pub hash_algorithm: HashAlgorithm,
pub internal_db: AveInternalDBConfig,
pub external_db: AveExternalDBConfig,
pub network: NetworkConfig,
pub contracts_path: PathBuf,
pub always_accept: bool,
pub safe_mode: bool,
pub tracking_size: usize,
pub is_service: bool,
pub only_clear_events: bool,
pub sync: SyncConfig,
pub spec: Option<MachineSpec>,
}
impl Default for Config {
fn default() -> Self {
Self {
keypair_algorithm: KeyPairAlgorithm::Ed25519,
hash_algorithm: HashAlgorithm::Blake3,
internal_db: Default::default(),
external_db: Default::default(),
network: Default::default(),
contracts_path: PathBuf::from("contracts"),
always_accept: Default::default(),
safe_mode: false,
tracking_size: 100,
is_service: false,
only_clear_events: false,
sync: Default::default(),
spec: None,
}
}
}
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(default)]
#[serde(rename_all = "snake_case")]
pub struct SyncConfig {
pub ledger_batch_size: usize,
pub governance: GovernanceSyncConfig,
pub tracker: TrackerSyncConfig,
pub update: UpdateSyncConfig,
pub reboot: RebootSyncConfig,
}
impl Default for SyncConfig {
fn default() -> Self {
Self {
ledger_batch_size: 100,
governance: GovernanceSyncConfig::default(),
tracker: TrackerSyncConfig::default(),
update: UpdateSyncConfig::default(),
reboot: RebootSyncConfig::default(),
}
}
}
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(default)]
#[serde(rename_all = "snake_case")]
pub struct UpdateSyncConfig {
pub round_retry_interval_secs: u64,
pub max_round_retries: usize,
pub witness_retry_count: usize,
pub witness_retry_interval_secs: u64,
}
impl Default for UpdateSyncConfig {
fn default() -> Self {
Self {
round_retry_interval_secs: 8,
max_round_retries: 3,
witness_retry_count: 1,
witness_retry_interval_secs: 5,
}
}
}
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(default)]
#[serde(rename_all = "snake_case")]
pub struct RebootSyncConfig {
pub stability_check_interval_secs: u64,
pub stability_check_max_retries: u64,
pub diff_retry_schedule_secs: Vec<u64>,
pub timeout_retry_schedule_secs: Vec<u64>,
}
impl Default for RebootSyncConfig {
fn default() -> Self {
Self {
stability_check_interval_secs: 5,
stability_check_max_retries: 3,
diff_retry_schedule_secs: vec![10, 20, 30, 60],
timeout_retry_schedule_secs:
default_reboot_timeout_retry_schedule_secs(),
}
}
}
#[cfg(any(test, feature = "test"))]
fn default_reboot_timeout_retry_schedule_secs() -> Vec<u64> {
vec![5, 5, 5, 5]
}
#[cfg(not(any(test, feature = "test")))]
fn default_reboot_timeout_retry_schedule_secs() -> Vec<u64> {
vec![30, 60, 120, 300]
}
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(default)]
#[serde(rename_all = "snake_case")]
pub struct GovernanceSyncConfig {
pub interval_secs: u64,
pub sample_size: usize,
pub response_timeout_secs: u64,
}
impl Default for GovernanceSyncConfig {
fn default() -> Self {
Self {
interval_secs: 60,
sample_size: 3,
response_timeout_secs: 10,
}
}
}
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(default)]
#[serde(rename_all = "snake_case")]
pub struct TrackerSyncConfig {
pub interval_secs: u64,
pub page_size: usize,
pub response_timeout_secs: u64,
pub update_batch_size: usize,
pub update_timeout_secs: u64,
}
impl Default for TrackerSyncConfig {
fn default() -> Self {
Self {
interval_secs: 30,
page_size: 50,
response_timeout_secs: 10,
update_batch_size: 2,
update_timeout_secs: 10,
}
}
}
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(rename_all = "snake_case")]
pub enum MachineSpec {
Profile(MachineProfile),
Custom {
ram_mb: u64,
cpu_cores: usize,
},
}
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum MachineProfile {
Nano,
Micro,
Small,
Medium,
Large,
XLarge,
#[serde(rename = "2xlarge")]
XXLarge,
}
impl MachineProfile {
pub const fn ram_mb(self) -> u64 {
match self {
Self::Nano => 512,
Self::Micro => 1_024,
Self::Small => 2_048,
Self::Medium => 4_096,
Self::Large => 8_192,
Self::XLarge => 16_384,
Self::XXLarge => 32_768,
}
}
pub const fn cpu_cores(self) -> usize {
match self {
Self::Nano => 2,
Self::Micro => 2,
Self::Small => 2,
Self::Medium => 2,
Self::Large => 2,
Self::XLarge => 4,
Self::XXLarge => 8,
}
}
}
impl Display for MachineProfile {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Nano => write!(f, "nano"),
Self::Micro => write!(f, "micro"),
Self::Small => write!(f, "small"),
Self::Medium => write!(f, "medium"),
Self::Large => write!(f, "large"),
Self::XLarge => write!(f, "xlarge"),
Self::XXLarge => write!(f, "2xlarge"),
}
}
}
pub struct ResolvedSpec {
pub ram_mb: u64,
pub cpu_cores: usize,
}
pub fn resolve_spec(spec: Option<&MachineSpec>) -> ResolvedSpec {
match spec {
Some(MachineSpec::Profile(p)) => ResolvedSpec {
ram_mb: p.ram_mb(),
cpu_cores: p.cpu_cores(),
},
Some(MachineSpec::Custom { ram_mb, cpu_cores }) => ResolvedSpec {
ram_mb: *ram_mb,
cpu_cores: *cpu_cores,
},
None => ResolvedSpec {
ram_mb: detect_ram_mb(),
cpu_cores: detect_cpu_cores(),
},
}
}
pub(crate) fn detect_ram_mb() -> u64 {
#[cfg(target_os = "linux")]
{
if let Ok(meminfo) = std::fs::read_to_string("/proc/meminfo") {
for line in meminfo.lines() {
if let Some(rest) = line.strip_prefix("MemTotal:")
&& let Some(kb_str) = rest.split_whitespace().next()
&& let Ok(kb) = kb_str.parse::<u64>()
{
return kb / 1024;
}
}
}
}
4_096
}
pub(crate) fn detect_cpu_cores() -> usize {
std::thread::available_parallelism()
.map(|n| n.get())
.unwrap_or(2)
}
impl From<MachineProfile> for ave_network::MachineProfile {
fn from(p: MachineProfile) -> Self {
match p {
MachineProfile::Nano => Self::Nano,
MachineProfile::Micro => Self::Micro,
MachineProfile::Small => Self::Small,
MachineProfile::Medium => Self::Medium,
MachineProfile::Large => Self::Large,
MachineProfile::XLarge => Self::XLarge,
MachineProfile::XXLarge => Self::XXLarge,
}
}
}
impl From<MachineSpec> for ave_network::MachineSpec {
fn from(spec: MachineSpec) -> Self {
match spec {
MachineSpec::Profile(p) => Self::Profile(p.into()),
MachineSpec::Custom { ram_mb, cpu_cores } => {
Self::Custom { ram_mb, cpu_cores }
}
}
}
}
impl From<MachineProfile> for ave_actors::MachineProfile {
fn from(p: MachineProfile) -> Self {
match p {
MachineProfile::Nano => Self::Nano,
MachineProfile::Micro => Self::Micro,
MachineProfile::Small => Self::Small,
MachineProfile::Medium => Self::Medium,
MachineProfile::Large => Self::Large,
MachineProfile::XLarge => Self::XLarge,
MachineProfile::XXLarge => Self::XXLarge,
}
}
}
impl From<MachineSpec> for ave_actors::MachineSpec {
fn from(spec: MachineSpec) -> Self {
match spec {
MachineSpec::Profile(p) => Self::Profile(p.into()),
MachineSpec::Custom { ram_mb, cpu_cores } => {
Self::Custom { ram_mb, cpu_cores }
}
}
}
}
#[derive(Debug, Clone, Deserialize, Serialize, Default)]
#[serde(default)]
pub struct AveInternalDBConfig {
#[serde(deserialize_with = "AveInternalDBFeatureConfig::deserialize_db")]
pub db: AveInternalDBFeatureConfig,
pub durability: bool,
}
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)]
pub enum AveInternalDBFeatureConfig {
#[cfg(feature = "rocksdb")]
Rocksdb {
path: PathBuf,
},
#[cfg(feature = "sqlite")]
Sqlite {
path: PathBuf,
},
}
impl Default for AveInternalDBFeatureConfig {
fn default() -> Self {
#[cfg(feature = "rocksdb")]
return AveInternalDBFeatureConfig::Rocksdb {
path: PathBuf::from("db").join("local").join("rocksdb"),
};
#[cfg(feature = "sqlite")]
return Self::Sqlite {
path: PathBuf::from("db").join("local").join("sqlite"),
};
}
}
impl AveInternalDBFeatureConfig {
pub fn build(path: &PathBuf) -> Self {
#[cfg(feature = "rocksdb")]
return AveInternalDBFeatureConfig::Rocksdb {
path: path.to_owned(),
};
#[cfg(feature = "sqlite")]
return Self::Sqlite {
path: path.to_owned(),
};
}
pub fn deserialize_db<'de, D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let path: String = String::deserialize(deserializer)?;
#[cfg(feature = "rocksdb")]
return Ok(AveInternalDBFeatureConfig::Rocksdb {
path: PathBuf::from(path),
});
#[cfg(feature = "sqlite")]
return Ok(Self::Sqlite {
path: PathBuf::from(path),
});
}
}
impl fmt::Display for AveInternalDBFeatureConfig {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
#[cfg(feature = "rocksdb")]
AveInternalDBFeatureConfig::Rocksdb { .. } => write!(f, "Rocksdb"),
#[cfg(feature = "sqlite")]
Self::Sqlite { .. } => write!(f, "Sqlite"),
}
}
}
#[derive(Debug, Clone, Deserialize, Serialize, Default)]
#[serde(default)]
pub struct AveExternalDBConfig {
#[serde(deserialize_with = "AveExternalDBFeatureConfig::deserialize_db")]
pub db: AveExternalDBFeatureConfig,
pub durability: bool,
}
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)]
pub enum AveExternalDBFeatureConfig {
#[cfg(feature = "ext-sqlite")]
Sqlite {
path: PathBuf,
},
}
impl Default for AveExternalDBFeatureConfig {
fn default() -> Self {
#[cfg(feature = "ext-sqlite")]
return Self::Sqlite {
path: PathBuf::from("db").join("ext").join("sqlite"),
};
}
}
impl AveExternalDBFeatureConfig {
pub fn build(path: &PathBuf) -> Self {
#[cfg(feature = "ext-sqlite")]
return Self::Sqlite {
path: path.to_owned(),
};
}
pub fn deserialize_db<'de, D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let path: String = String::deserialize(deserializer)?;
#[cfg(feature = "ext-sqlite")]
return Ok(Self::Sqlite {
path: PathBuf::from(path),
});
}
}
impl fmt::Display for AveExternalDBFeatureConfig {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Sqlite")
}
}
#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)]
pub struct LoggingOutput {
pub stdout: bool,
pub file: bool,
pub api: bool,
}
impl Default for LoggingOutput {
fn default() -> Self {
Self {
stdout: true,
file: Default::default(),
api: Default::default(),
}
}
}
#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Default, Serialize)]
#[serde(rename_all = "lowercase")]
pub enum LoggingRotation {
#[default]
Size,
Hourly,
Daily,
Weekly,
Monthly,
Yearly,
Never,
}
impl Display for LoggingRotation {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Size => write!(f, "size"),
Self::Hourly => write!(f, "hourly"),
Self::Daily => write!(f, "daily"),
Self::Weekly => write!(f, "weekly"),
Self::Monthly => write!(f, "monthly"),
Self::Yearly => write!(f, "yearly"),
Self::Never => write!(f, "never"),
}
}
}
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(default)]
pub struct LoggingConfig {
pub output: LoggingOutput,
pub api_url: Option<String>,
pub file_path: PathBuf, pub rotation: LoggingRotation,
pub max_size: usize, pub max_files: usize, pub level: String,
}
impl Default for LoggingConfig {
fn default() -> Self {
Self {
output: LoggingOutput::default(),
api_url: None,
file_path: PathBuf::from("logs"),
rotation: LoggingRotation::default(),
max_size: 100 * 1024 * 1024,
max_files: 3,
level: "info".to_string(),
}
}
}
impl LoggingConfig {
pub const fn logs(&self) -> bool {
self.output.api || self.output.file || self.output.stdout
}
}
#[derive(Clone, Debug, Deserialize, Default, Eq, PartialEq, Serialize)]
#[serde(default)]
pub struct SinkServer {
pub server: String,
pub events: BTreeSet<SinkTypes>,
pub url: String,
pub auth: bool,
#[serde(default = "default_sink_concurrency")]
pub concurrency: usize,
#[serde(default = "default_sink_queue_capacity")]
pub queue_capacity: usize,
#[serde(default)]
pub queue_policy: SinkQueuePolicy,
#[serde(default)]
pub routing_strategy: SinkRoutingStrategy,
#[serde(default = "default_sink_connect_timeout_ms")]
pub connect_timeout_ms: u64,
#[serde(default = "default_sink_request_timeout_ms")]
pub request_timeout_ms: u64,
#[serde(default = "default_sink_max_retries")]
pub max_retries: usize,
}
#[derive(Default)]
pub struct SinkAuth {
pub sink: SinkConfig,
pub token: Option<TokenResponse>,
pub password: String,
pub api_key: String,
}
#[derive(Clone, Debug, Deserialize, Default, Serialize)]
#[serde(default)]
pub struct SinkConfig {
pub sinks: BTreeMap<String, Vec<SinkServer>>,
pub auth: String,
pub username: String,
}
#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum SinkQueuePolicy {
DropOldest,
#[default]
DropNewest,
}
#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum SinkRoutingStrategy {
#[default]
OrderedBySubject,
UnorderedRoundRobin,
}
const fn default_sink_concurrency() -> usize {
2
}
const fn default_sink_queue_capacity() -> usize {
1024
}
const fn default_sink_connect_timeout_ms() -> u64 {
2_000
}
const fn default_sink_request_timeout_ms() -> u64 {
5_000
}
const fn default_sink_max_retries() -> usize {
2
}