use serde::{Deserialize, Serialize};
pub const DEFAULT_MAX_DEPTH: u32 = 10;
pub const DEFAULT_MAX_CARDINALITY: usize = 100_000;
pub const DEFAULT_MEMORY_LIMIT_BYTES: usize = 100 * 1024 * 1024;
pub const DEFAULT_RATE_LIMIT_QPS: u32 = 100;
pub const DEFAULT_CIRCUIT_FAILURE_THRESHOLD: u32 = 5;
pub const DEFAULT_CIRCUIT_RECOVERY_SECONDS: u64 = 30;
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(default)]
pub struct QueryLimits {
pub max_depth: u32,
pub max_cardinality: usize,
pub memory_limit_bytes: usize,
pub timeout_ms: u64,
pub rate_limit_qps: u32,
pub circuit_failure_threshold: u32,
pub circuit_recovery_seconds: u64,
}
impl Default for QueryLimits {
fn default() -> Self {
Self {
max_depth: DEFAULT_MAX_DEPTH,
max_cardinality: DEFAULT_MAX_CARDINALITY,
memory_limit_bytes: DEFAULT_MEMORY_LIMIT_BYTES,
timeout_ms: 30_000,
rate_limit_qps: DEFAULT_RATE_LIMIT_QPS,
circuit_failure_threshold: DEFAULT_CIRCUIT_FAILURE_THRESHOLD,
circuit_recovery_seconds: DEFAULT_CIRCUIT_RECOVERY_SECONDS,
}
}
}
impl QueryLimits {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn with_max_depth(mut self, depth: u32) -> Self {
self.max_depth = depth;
self
}
#[must_use]
pub fn with_max_cardinality(mut self, cardinality: usize) -> Self {
self.max_cardinality = cardinality;
self
}
#[must_use]
pub fn with_memory_limit(mut self, bytes: usize) -> Self {
self.memory_limit_bytes = bytes;
self
}
#[must_use]
pub fn with_timeout_ms(mut self, ms: u64) -> Self {
self.timeout_ms = ms;
self
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum GuardRailViolation {
DepthExceeded {
max: u32,
actual: u32,
},
CardinalityExceeded {
max: usize,
actual: usize,
},
MemoryExceeded {
max_bytes: usize,
used_bytes: usize,
},
Timeout {
max_ms: u64,
elapsed_ms: u64,
},
RateLimitExceeded {
limit_qps: u32,
},
CircuitOpen {
recovery_in_seconds: u64,
},
}
impl std::fmt::Display for GuardRailViolation {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::DepthExceeded { max, actual } => {
write!(f, "Traversal depth exceeded: max={max}, actual={actual}")
}
Self::CardinalityExceeded { max, actual } => {
write!(f, "Cardinality exceeded: max={max}, actual={actual}")
}
Self::MemoryExceeded {
max_bytes,
used_bytes,
} => {
write!(
f,
"Memory limit exceeded: max={}MB, used={}MB",
max_bytes / (1024 * 1024),
used_bytes / (1024 * 1024)
)
}
Self::Timeout { max_ms, elapsed_ms } => {
write!(f, "Query timed out: max={max_ms}ms, elapsed={elapsed_ms}ms")
}
Self::RateLimitExceeded { limit_qps } => {
write!(f, "Rate limit exceeded: {limit_qps} queries/second")
}
Self::CircuitOpen {
recovery_in_seconds,
} => {
write!(
f,
"Circuit breaker open, recovery in {recovery_in_seconds}s"
)
}
}
}
}
impl std::error::Error for GuardRailViolation {}