2026-04-07 02:50:19 +08:00
|
|
|
use aether_data_contracts::DataLayerError;
|
2026-04-05 20:23:16 +08:00
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
|
|
|
pub struct UsageRuntimeConfig {
|
|
|
|
|
pub enabled: bool,
|
|
|
|
|
pub stream_key: String,
|
|
|
|
|
pub consumer_group: String,
|
|
|
|
|
pub dlq_stream_key: String,
|
|
|
|
|
pub stream_maxlen: usize,
|
|
|
|
|
pub consumer_batch_size: usize,
|
|
|
|
|
pub consumer_block_ms: u64,
|
|
|
|
|
pub reclaim_idle_ms: u64,
|
|
|
|
|
pub reclaim_count: usize,
|
|
|
|
|
pub reclaim_interval_ms: u64,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Default for UsageRuntimeConfig {
|
|
|
|
|
fn default() -> Self {
|
|
|
|
|
Self {
|
|
|
|
|
enabled: false,
|
|
|
|
|
stream_key: "usage:events".to_string(),
|
|
|
|
|
consumer_group: "usage_consumers".to_string(),
|
|
|
|
|
dlq_stream_key: "usage:events:dlq".to_string(),
|
|
|
|
|
stream_maxlen: 2_000,
|
|
|
|
|
consumer_batch_size: 200,
|
|
|
|
|
consumer_block_ms: 500,
|
|
|
|
|
reclaim_idle_ms: 30_000,
|
|
|
|
|
reclaim_count: 200,
|
|
|
|
|
reclaim_interval_ms: 5_000,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl UsageRuntimeConfig {
|
|
|
|
|
pub fn disabled() -> Self {
|
|
|
|
|
Self::default()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn validate(&self) -> Result<(), DataLayerError> {
|
|
|
|
|
if !self.enabled {
|
|
|
|
|
return Ok(());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if self.stream_key.trim().is_empty() {
|
|
|
|
|
return Err(DataLayerError::InvalidConfiguration(
|
|
|
|
|
"usage runtime stream_key cannot be empty".to_string(),
|
|
|
|
|
));
|
|
|
|
|
}
|
|
|
|
|
if self.consumer_group.trim().is_empty() {
|
|
|
|
|
return Err(DataLayerError::InvalidConfiguration(
|
|
|
|
|
"usage runtime consumer_group cannot be empty".to_string(),
|
|
|
|
|
));
|
|
|
|
|
}
|
|
|
|
|
if self.dlq_stream_key.trim().is_empty() {
|
|
|
|
|
return Err(DataLayerError::InvalidConfiguration(
|
|
|
|
|
"usage runtime dlq_stream_key cannot be empty".to_string(),
|
|
|
|
|
));
|
|
|
|
|
}
|
|
|
|
|
if self.stream_maxlen == 0 {
|
|
|
|
|
return Err(DataLayerError::InvalidConfiguration(
|
|
|
|
|
"usage runtime stream_maxlen must be positive".to_string(),
|
|
|
|
|
));
|
|
|
|
|
}
|
|
|
|
|
if self.consumer_batch_size == 0 {
|
|
|
|
|
return Err(DataLayerError::InvalidConfiguration(
|
|
|
|
|
"usage runtime consumer_batch_size must be positive".to_string(),
|
|
|
|
|
));
|
|
|
|
|
}
|
|
|
|
|
if self.consumer_block_ms == 0 {
|
|
|
|
|
return Err(DataLayerError::InvalidConfiguration(
|
|
|
|
|
"usage runtime consumer_block_ms must be positive".to_string(),
|
|
|
|
|
));
|
|
|
|
|
}
|
|
|
|
|
if self.reclaim_idle_ms == 0 {
|
|
|
|
|
return Err(DataLayerError::InvalidConfiguration(
|
|
|
|
|
"usage runtime reclaim_idle_ms must be positive".to_string(),
|
|
|
|
|
));
|
|
|
|
|
}
|
|
|
|
|
if self.reclaim_count == 0 {
|
|
|
|
|
return Err(DataLayerError::InvalidConfiguration(
|
|
|
|
|
"usage runtime reclaim_count must be positive".to_string(),
|
|
|
|
|
));
|
|
|
|
|
}
|
|
|
|
|
if self.reclaim_interval_ms == 0 {
|
|
|
|
|
return Err(DataLayerError::InvalidConfiguration(
|
|
|
|
|
"usage runtime reclaim_interval_ms must be positive".to_string(),
|
|
|
|
|
));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
|
mod tests {
|
|
|
|
|
use super::UsageRuntimeConfig;
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn disabled_config_is_valid() {
|
|
|
|
|
assert!(UsageRuntimeConfig::disabled().validate().is_ok());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn enabled_config_rejects_empty_stream_key() {
|
|
|
|
|
let config = UsageRuntimeConfig {
|
|
|
|
|
enabled: true,
|
|
|
|
|
stream_key: String::new(),
|
|
|
|
|
..UsageRuntimeConfig::default()
|
|
|
|
|
};
|
|
|
|
|
assert!(config.validate().is_err());
|
|
|
|
|
}
|
|
|
|
|
}
|