use core::time::Duration;
use serde::{Deserialize, Serialize};
pub const DEFAULT_MIN_DELTAS_BEFORE_COMPACT: usize = 10_000;
pub const DEFAULT_RETAIN_RECENT_COUNT: usize = 1_000;
pub const DEFAULT_CHECK_INTERVAL_SECS: u64 = 3_600;
#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
#[non_exhaustive]
pub struct DagCompactionConfig {
#[serde(default = "default_enabled")]
pub enabled: bool,
#[serde(default = "default_min_deltas_before_compact")]
pub min_deltas_before_compact: usize,
#[serde(default = "default_retain_recent_count")]
pub retain_recent_count: usize,
#[serde(default = "default_check_interval", with = "duration_secs")]
pub check_interval: Duration,
}
impl Default for DagCompactionConfig {
fn default() -> Self {
Self {
enabled: default_enabled(),
min_deltas_before_compact: DEFAULT_MIN_DELTAS_BEFORE_COMPACT,
retain_recent_count: DEFAULT_RETAIN_RECENT_COUNT,
check_interval: default_check_interval(),
}
}
}
impl DagCompactionConfig {
#[must_use]
pub fn is_valid(&self) -> bool {
self.retain_recent_count < self.min_deltas_before_compact && !self.check_interval.is_zero()
}
}
const fn default_enabled() -> bool {
true
}
const fn default_min_deltas_before_compact() -> usize {
DEFAULT_MIN_DELTAS_BEFORE_COMPACT
}
const fn default_retain_recent_count() -> usize {
DEFAULT_RETAIN_RECENT_COUNT
}
const fn default_check_interval() -> Duration {
Duration::from_secs(DEFAULT_CHECK_INTERVAL_SECS)
}
mod duration_secs {
use core::time::Duration;
use serde::{Deserialize, Deserializer, Serializer};
pub fn serialize<S: Serializer>(value: &Duration, serializer: S) -> Result<S::Ok, S::Error> {
serializer.serialize_u64(value.as_secs())
}
pub fn deserialize<'de, D: Deserializer<'de>>(deserializer: D) -> Result<Duration, D::Error> {
let secs = u64::deserialize(deserializer)?;
Ok(Duration::from_secs(secs))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn default_is_enabled_with_sane_thresholds() {
let cfg = DagCompactionConfig::default();
assert!(cfg.enabled);
assert_eq!(
cfg.min_deltas_before_compact,
DEFAULT_MIN_DELTAS_BEFORE_COMPACT
);
assert_eq!(cfg.retain_recent_count, DEFAULT_RETAIN_RECENT_COUNT);
assert_eq!(cfg.check_interval.as_secs(), DEFAULT_CHECK_INTERVAL_SECS);
assert!(cfg.is_valid());
}
#[test]
fn retain_at_or_above_min_is_invalid() {
let mut cfg = DagCompactionConfig::default();
cfg.retain_recent_count = cfg.min_deltas_before_compact;
assert!(!cfg.is_valid());
cfg.retain_recent_count = cfg.min_deltas_before_compact + 1;
assert!(!cfg.is_valid());
}
#[test]
fn zero_check_interval_is_invalid() {
let cfg = DagCompactionConfig {
check_interval: Duration::ZERO,
..DagCompactionConfig::default()
};
assert!(!cfg.is_valid());
}
#[test]
fn absent_fields_fall_back_to_defaults() {
let cfg: DagCompactionConfig = serde_json::from_str("{}").expect("empty object");
assert!(cfg.enabled);
assert_eq!(
cfg.min_deltas_before_compact,
DEFAULT_MIN_DELTAS_BEFORE_COMPACT
);
assert_eq!(cfg.check_interval.as_secs(), DEFAULT_CHECK_INTERVAL_SECS);
}
#[test]
fn explicit_disable_is_respected() {
let cfg: DagCompactionConfig =
serde_json::from_str(r#"{"enabled": false}"#).expect("explicit disable");
assert!(!cfg.enabled);
}
#[test]
fn check_interval_round_trips_as_seconds() {
let cfg = DagCompactionConfig {
enabled: true,
min_deltas_before_compact: 5000,
retain_recent_count: 500,
check_interval: Duration::from_secs(900),
};
let json = serde_json::to_string(&cfg).expect("serialize");
assert!(json.contains("\"check_interval\":900"));
let back: DagCompactionConfig = serde_json::from_str(&json).expect("deserialize");
assert_eq!(back.check_interval.as_secs(), 900);
assert!(back.enabled);
assert!(back.is_valid());
}
}