pharos-server 0.1.0

Listeners, Plugin Chain contract and metrics for the Pharos DNS server
Documentation
#![forbid(unsafe_code)]

use pharos_server::metrics::Registry;
use stats_alloc::{INSTRUMENTED_SYSTEM, StatsAlloc};
use std::alloc::System;

#[global_allocator]
static GLOBAL: &StatsAlloc<System> = &INSTRUMENTED_SYSTEM;

#[test]
fn cache_metrics_are_shared_escaped_and_allocation_free() {
    let registry = Registry::new("v", "r", "rust");
    let cache = registry.cache("dns://:53", "example.\n", "a\"b");
    let same = registry.cache("dns://:53", "example.\n", "a\"b").clone();
    let before = GLOBAL.stats();
    cache.request();
    same.request();
    cache.hit(false);
    cache.hit(true);
    cache.miss();
    cache.prefetch();
    cache.drop_response();
    cache.served_stale();
    cache.evict(false);
    cache.evict(true);
    cache.entries(false, 1_000_000);
    cache.entries(true, 5);
    cache.entries(true, 0);
    let after = GLOBAL.stats();
    assert_eq!(after.allocations - before.allocations, 0);
    assert_eq!(after.reallocations - before.reallocations, 0);

    let mut out = String::new();
    registry.encode(&mut out);
    for expected in [
        "# HELP pharos_cache_entries The number of elements in the cache.",
        "# TYPE pharos_cache_entries gauge",
        "# HELP pharos_cache_requests_total The count of cache requests.",
        "# HELP pharos_cache_hits_total The count of cache hits.",
        "# HELP pharos_cache_misses_total The count of cache misses. Deprecated, derive misses from cache hits/requests counters.",
        "# HELP pharos_cache_prefetch_total The number of times the cache has prefetched a cached item.",
        "# HELP pharos_cache_drops_total The number responses that are not cached, because the reply is malformed.",
        "# HELP pharos_cache_served_stale_total The number of requests served from stale cache entries.",
        "# HELP pharos_cache_evictions_total The count of cache evictions.",
        "# TYPE pharos_cache_hits_total counter",
        "pharos_cache_entries{server=\"dns://:53\",type=\"success\",view=\"a\\\"b\",zones=\"example.\\n\"} 1e+06",
        "pharos_cache_entries{server=\"dns://:53\",type=\"denial\",view=\"a\\\"b\",zones=\"example.\\n\"} 0",
        "pharos_cache_hits_total{server=\"dns://:53\",type=\"success\",view=\"a\\\"b\",zones=\"example.\\n\"} 1",
        "pharos_cache_hits_total{server=\"dns://:53\",type=\"denial\",view=\"a\\\"b\",zones=\"example.\\n\"} 1",
        "pharos_cache_evictions_total{server=\"dns://:53\",type=\"success\",view=\"a\\\"b\",zones=\"example.\\n\"} 1",
        "pharos_cache_evictions_total{server=\"dns://:53\",type=\"denial\",view=\"a\\\"b\",zones=\"example.\\n\"} 1",
        "pharos_cache_requests_total{server=\"dns://:53\",view=\"a\\\"b\",zones=\"example.\\n\"} 2",
        "pharos_cache_misses_total{server=\"dns://:53\",view=\"a\\\"b\",zones=\"example.\\n\"} 1",
        "pharos_cache_prefetch_total{server=\"dns://:53\",view=\"a\\\"b\",zones=\"example.\\n\"} 1",
        "pharos_cache_drops_total{server=\"dns://:53\",view=\"a\\\"b\",zones=\"example.\\n\"} 1",
        "pharos_cache_served_stale_total{server=\"dns://:53\",view=\"a\\\"b\",zones=\"example.\\n\"} 1",
    ] {
        assert!(
            out.lines().any(|line| line == expected),
            "missing {expected}\n{out}"
        );
    }
    cache_series_require_configuration_and_are_isolated_by_labels();
    concurrent_cache_recording_preserves_counts();
}

fn concurrent_cache_recording_preserves_counts() {
    let registry = Registry::new("v", "r", "rust");
    let cache = registry.cache("s", ".", "");
    std::thread::scope(|scope| {
        for _ in 0..4 {
            let cache = cache.clone();
            scope.spawn(move || {
                for _ in 0..1000 {
                    cache.request();
                    cache.hit(true);
                }
            });
        }
    });
    let mut out = String::new();
    registry.encode(&mut out);
    assert!(out.contains("pharos_cache_requests_total{server=\"s\",view=\"\",zones=\".\"} 4000\n"));
    assert!(out.contains(
        "pharos_cache_hits_total{server=\"s\",type=\"denial\",view=\"\",zones=\".\"} 4000\n"
    ));
}

fn cache_series_require_configuration_and_are_isolated_by_labels() {
    let registry = Registry::new("v", "r", "rust");
    let mut out = String::new();
    registry.encode(&mut out);
    assert!(!out.contains("pharos_cache_"));

    registry.cache("s", ".", "one").request();
    registry.cache("s", ".", "two").request();
    registry.cache("s", "other.", "one").request();
    out.clear();
    registry.encode(&mut out);
    for labels in [
        "server=\"s\",view=\"one\",zones=\".\"",
        "server=\"s\",view=\"two\",zones=\".\"",
        "server=\"s\",view=\"one\",zones=\"other.\"",
    ] {
        assert!(out.contains(&format!("pharos_cache_requests_total{{{labels}}} 1\n")));
    }
}