Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
106 changes: 105 additions & 1 deletion jans-cedarling/cedarling/src/data/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use std::time::Duration;

/// Configuration for the DataStore component.
///
/// Controls storage limits, TTL behavior, and capacity management.
/// Controls storage limits, TTL behavior, capacity management, and metrics.
///
/// ## TTL Semantics
///
Expand Down Expand Up @@ -47,6 +47,8 @@ pub struct DataStoreConfig {
/// Maximum allowed TTL.
/// `None` means no upper limit on TTL values (uses 10 years).
pub max_ttl: Option<Duration>,
/// Enable metrics tracking (access counts, timestamps)
pub enable_metrics: bool,
}

impl Default for DataStoreConfig {
Expand All @@ -56,10 +58,43 @@ impl Default for DataStoreConfig {
max_entry_size: 1_048_576, // 1MB
default_ttl: None,
max_ttl: Some(Duration::from_secs(3600)), // 1 hour
enable_metrics: true,
}
}
}

/// Error returned when DataStoreConfig validation fails.
#[derive(Debug, thiserror::Error)]
pub enum ConfigValidationError {
/// default_ttl exceeds max_ttl
#[error("default_ttl ({default:?}) exceeds max_ttl ({max:?})")]
DefaultTtlExceedsMax {
/// The default TTL value that exceeds the maximum
default: Duration,
/// The maximum TTL value
max: Duration,
},
}

impl DataStoreConfig {
/// Validate the configuration for consistency.
///
/// # Errors
///
/// Returns `ConfigValidationError` if:
/// - `default_ttl` exceeds `max_ttl` (when both are Some)
pub fn validate(&self) -> Result<(), ConfigValidationError> {
// Check if default_ttl exceeds max_ttl
if let (Some(default), Some(max)) = (self.default_ttl, self.max_ttl) {
if default > max {
return Err(ConfigValidationError::DefaultTtlExceedsMax { default, max });
}
}

Ok(())
}
}

#[cfg(test)]
mod tests {
use super::*;
Expand All @@ -71,5 +106,74 @@ mod tests {
assert_eq!(config.max_entry_size, 1_048_576);
assert_eq!(config.default_ttl, None);
assert_eq!(config.max_ttl, Some(Duration::from_secs(3600)));
assert!(config.enable_metrics);
}

#[test]
fn test_valid_config() {
let config = DataStoreConfig {
default_ttl: Some(Duration::from_secs(300)),
max_ttl: Some(Duration::from_secs(3600)),
..Default::default()
};
assert!(
matches!(config.validate(), Ok(_)),
"expected DataStoreConfig::validate() to succeed when default_ttl is less than max_ttl"
);
}

#[test]
fn test_default_ttl_exceeds_max() {
let config = DataStoreConfig {
default_ttl: Some(Duration::from_secs(7200)), // 2 hours
max_ttl: Some(Duration::from_secs(3600)), // 1 hour
..Default::default()
};
assert!(
matches!(
config.validate(),
Err(ConfigValidationError::DefaultTtlExceedsMax { .. })
),
"expected DataStoreConfig::validate() to return ConfigValidationError when default_ttl exceeds max_ttl"
);
}

#[test]
fn test_none_ttls_are_valid() {
let config = DataStoreConfig {
default_ttl: None,
max_ttl: None,
..Default::default()
};
assert!(
matches!(config.validate(), Ok(_)),
"expected DataStoreConfig::validate() to succeed when both TTL values are None"
);
}

#[test]
fn test_only_default_ttl_is_valid() {
let config = DataStoreConfig {
default_ttl: Some(Duration::from_secs(300)),
max_ttl: None,
..Default::default()
};
assert!(
matches!(config.validate(), Ok(_)),
"expected DataStoreConfig::validate() to succeed when only default_ttl is set"
);
}

#[test]
fn test_only_max_ttl_is_valid() {
let config = DataStoreConfig {
default_ttl: None,
max_ttl: Some(Duration::from_secs(3600)),
..Default::default()
};
assert!(
matches!(config.validate(), Ok(_)),
"expected DataStoreConfig::validate() to succeed when only max_ttl is set"
);
}
}
Loading