diff --git a/Cargo.lock b/Cargo.lock index 37c4faf7..b01a32a5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "addr2line" @@ -1580,6 +1580,7 @@ dependencies = [ "kumo-log-types", "libunbound", "lruttl", + "prometheus", "rand", "serde", "thiserror 1.0.69", diff --git a/crates/dns-resolver/Cargo.toml b/crates/dns-resolver/Cargo.toml index acd07f5c..0ec329e4 100644 --- a/crates/dns-resolver/Cargo.toml +++ b/crates/dns-resolver/Cargo.toml @@ -20,6 +20,7 @@ async-trait = {workspace=true} kumo-log-types = {path="../kumo-log-types"} libunbound = {workspace=true, optional=true} lruttl = {path="../lruttl"} +prometheus.workspace = true rand = {workspace=true} serde = {workspace=true} thiserror = {workspace=true} diff --git a/crates/dns-resolver/src/lib.rs b/crates/dns-resolver/src/lib.rs index e621f1bc..95199ef7 100644 --- a/crates/dns-resolver/src/lib.rs +++ b/crates/dns-resolver/src/lib.rs @@ -33,6 +33,43 @@ static IPV6_CACHE: LazyLock>>>> = static IP_CACHE: LazyLock>>>> = LazyLock::new(|| StdMutex::new(LruCacheWithTtl::new_named("dns_resolver_ip", 1024))); +static MX_IN_PROGRESS: LazyLock = LazyLock::new(|| { + prometheus::register_int_gauge!( + "dns_mx_resolve_in_progress", + "number of MailExchanger::resolve calls currently in progress" + ) + .unwrap() +}); +static MX_SUCCESS: LazyLock = LazyLock::new(|| { + prometheus::register_int_counter!( + "dns_mx_resolve_status_ok", + "total number of successful MailExchanger::resolve calls" + ) + .unwrap() +}); +static MX_FAIL: LazyLock = LazyLock::new(|| { + prometheus::register_int_counter!( + "dns_mx_resolve_status_fail", + "total number of failed MailExchanger::resolve calls" + ) + .unwrap() +}); +static MX_CACHED: LazyLock = LazyLock::new(|| { + prometheus::register_int_counter!( + "dns_mx_resolve_cache_hit", + "total number of MailExchanger::resolve calls satisfied by level 1 cache" + ) + .unwrap() +}); +static MX_QUERIES: LazyLock = LazyLock::new(|| { + prometheus::register_int_counter!( + "dns_mx_resolve_cache_miss", + "total number of MailExchanger::resolve calls that resulted in an \ + MX DNS request to the next level of cache" + ) + .unwrap() +}); + fn default_resolver() -> impl Resolver { #[cfg(feature = "default-unbound")] return UnboundResolver::new().unwrap(); @@ -191,6 +228,18 @@ pub async fn resolve_a_or_aaaa(domain_name: &str) -> anyhow::Result anyhow::Result> { + MX_IN_PROGRESS.inc(); + let result = Self::resolve_impl(domain_name).await; + MX_IN_PROGRESS.dec(); + if result.is_ok() { + MX_SUCCESS.inc(); + } else { + MX_FAIL.inc(); + } + result + } + + async fn resolve_impl(domain_name: &str) -> anyhow::Result> { if domain_name.starts_with('[') { // It's a literal address, no DNS lookup necessary @@ -252,10 +301,12 @@ impl MailExchanger { let name_fq = fully_qualify(domain_name)?; if let Some(mx) = mx_cache_get(&name_fq) { + MX_CACHED.inc(); return Ok(mx); } let start = Instant::now(); + MX_QUERIES.inc(); let (by_pref, expires) = match lookup_mx_record(&name_fq).await { Ok((by_pref, expires)) => (by_pref, expires), Err(err) => anyhow::bail!( diff --git a/docs/changelog/main.md b/docs/changelog/main.md index 0f98eae8..e3ff9236 100644 --- a/docs/changelog/main.md +++ b/docs/changelog/main.md @@ -21,6 +21,11 @@ This gives an opportunity to rebind or reject messages which are experiencing persistent DNS resolution issues. #319 +* New `dns_mx_resolve_in_progress`, `dns_mx_resolve_status_ok`, + `dns_mx_resolve_status_fail`, `dns_mx_resolve_cache_hit`, + `dns_mx_resolve_cache_miss` metrics that reflect the status of MX + resolution. These are available via the metrics endpoints. + ## Fixes * When `enable_tls` is set to `Required` or `RequiredInsecure`, ignore the