use std::time::{Duration, Instant};
pub const DEFAULT_TIMEOUT_MS: u64 = 180_000;
#[derive(Clone, Copy, Debug, Default)]
pub struct QueryDefaults {
pub timeout_ms: Option<u64>,
pub max_work_units: Option<usize>,
pub row_limit: Option<usize>,
}
pub struct ResolvedQueryOptions {
pub timeout_ms: Option<u64>,
pub deadline: Option<Instant>,
pub max_work_units: Option<usize>,
pub row_limit: Option<usize>,
}
pub fn deadline_from(timeout_ms: Option<u64>) -> Option<Instant> {
timeout_ms
.filter(|ms| *ms != 0)
.map(|ms| Instant::now() + Duration::from_millis(ms))
}
impl QueryDefaults {
pub fn resolve(
self,
timeout_ms: Option<u64>,
max_work_units: Option<usize>,
row_limit: Option<usize>,
) -> ResolvedQueryOptions {
let timeout_ms = timeout_ms.or(self.timeout_ms).or(Some(DEFAULT_TIMEOUT_MS));
ResolvedQueryOptions {
timeout_ms: timeout_ms.filter(|ms| *ms != 0),
deadline: deadline_from(timeout_ms),
max_work_units: max_work_units.or(self.max_work_units),
row_limit: row_limit.or(self.row_limit),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn literal_zero_and_optional_inheritance_have_distinct_meanings() {
let policy = QueryDefaults {
timeout_ms: Some(0),
max_work_units: Some(1),
row_limit: Some(2),
};
let inherited = policy.resolve(None, None, None);
assert!(inherited.deadline.is_none());
assert_eq!(inherited.max_work_units, Some(1));
assert_eq!(inherited.row_limit, Some(2));
let explicit = policy.resolve(Some(5), Some(0), Some(0));
assert!(explicit.deadline.is_some());
assert_eq!(explicit.max_work_units, Some(0));
assert_eq!(explicit.row_limit, Some(0));
assert!(deadline_from(Some(0)).is_none());
}
#[test]
fn an_unset_policy_resolves_to_the_shared_default() {
let resolved = QueryDefaults::default().resolve(None, None, None);
assert_eq!(resolved.timeout_ms, Some(DEFAULT_TIMEOUT_MS));
assert_eq!(DEFAULT_TIMEOUT_MS, 180_000);
let deadline = resolved.deadline.expect("the default supplies a deadline");
let remaining = deadline.saturating_duration_since(Instant::now());
assert!(
remaining <= Duration::from_millis(DEFAULT_TIMEOUT_MS)
&& remaining > Duration::from_millis(DEFAULT_TIMEOUT_MS - 5_000),
"expected roughly {DEFAULT_TIMEOUT_MS} ms out, got {remaining:?}"
);
}
}