use std::time::Duration;
use systemprompt_extension::{Migration, cost};
pub(crate) const DEFAULT_STATEMENT_TIMEOUT: Duration = Duration::from_secs(300);
pub(crate) const MEASURED_SAFETY_FACTOR: u32 = 10;
pub(crate) const MIN_DERIVED_TIMEOUT: Duration = Duration::from_secs(30);
pub(crate) const LOCK_TIMEOUT: Duration = Duration::from_secs(10);
pub(crate) const TIMEOUT_ENV: &str = "SYSTEMPROMPT_MIGRATION_STATEMENT_TIMEOUT_SECS";
#[must_use]
pub(crate) fn statement_timeout(migration: &Migration) -> Option<Duration> {
match env_timeout() {
EnvTimeout::Unset => {},
EnvTimeout::Disabled => return None,
EnvTimeout::Bounded(timeout) => return Some(timeout),
}
if let Ok(Some(declared)) = cost::parse(migration.sql) {
return Some(
declared
.measured
.saturating_mul(MEASURED_SAFETY_FACTOR)
.max(MIN_DERIVED_TIMEOUT),
);
}
Some(DEFAULT_STATEMENT_TIMEOUT)
}
enum EnvTimeout {
Unset,
Disabled,
Bounded(Duration),
}
fn env_timeout() -> EnvTimeout {
let Ok(raw) = std::env::var(TIMEOUT_ENV) else {
return EnvTimeout::Unset;
};
match raw.trim().parse::<u64>() {
Ok(0) => EnvTimeout::Disabled,
Ok(secs) => EnvTimeout::Bounded(Duration::from_secs(secs)),
Err(_) => EnvTimeout::Bounded(DEFAULT_STATEMENT_TIMEOUT),
}
}
#[must_use]
pub(crate) fn timeout_statements(timeout: Option<Duration>, local: bool) -> Vec<String> {
let scope = if local { "LOCAL " } else { "" };
let statement = timeout.map_or_else(|| "0".to_owned(), |d| format!("{}", d.as_millis()));
vec![
format!("SET {scope}statement_timeout = {statement}"),
format!("SET {scope}lock_timeout = {}", LOCK_TIMEOUT.as_millis()),
]
}