Skip to main content

trust_dns_resolver/name_server/
name_server_stats.rs

1// Copyright 2015-2019 Benjamin Fry <benjaminfry@me.com>
2//
3// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
4// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
5// http://opensource.org/licenses/MIT>, at your option. This file may not be
6// copied, modified, or distributed except according to those terms.
7
8use std::cmp::Ordering;
9
10use std::collections::VecDeque;
11use std::sync::Mutex;
12use std::sync::atomic::{self, AtomicUsize};
13
14use crate::error::ResolveErrorKind;
15
16/// query statistics for a single name server.
17#[derive(Debug, Clone)]
18pub struct NameServerStats {
19    /// The address of the name server.
20    pub addr: std::net::SocketAddr,
21    /// The protocol used to talk to the name server.
22    pub proto: crate::config::Protocol,
23    /// The total number of lookup failures.
24    pub failures: usize,
25    /// The total number of lookup successes.
26    pub successes: usize,
27    /// The number of successful queries since the last failure.
28    pub success_streak: usize,
29    /// The last `N` errors seen when querying this nameserver. Configured with
30    /// [`crate::config::NameServerConfig::num_retained_errors`].
31    pub recent_errors: Vec<ResolveErrorKind>,
32}
33
34#[derive(Clone)]
35pub(crate) struct InternalNameServerStats {
36    successes: usize,
37    failures: usize,
38    success_streak: usize,
39    retained_errors: usize,
40    recent_errors: VecDeque<ResolveErrorKind>,
41    // TODO: incorporate latency
42}
43
44impl InternalNameServerStats {
45    pub(crate) fn new(retained_errors: usize) -> Self {
46        Self::new_internal(0, 0, 0, retained_errors)
47    }
48
49    fn new_internal(successes: usize, failures: usize, success_streak: usize, retained_errors: usize) -> Self {
50        Self {
51            successes: successes,
52            failures: failures,
53            success_streak: success_streak,
54            retained_errors: retained_errors,
55            recent_errors: VecDeque::with_capacity(retained_errors),
56        }
57    }
58
59    pub(crate) fn next_success(&mut self) {
60        self.successes += 1;
61        self.success_streak += 1;
62    }
63
64    pub(crate) fn next_failure(&mut self, error: ResolveErrorKind) {
65        self.failures += 1;
66        self.success_streak = 0;
67
68        if self.retained_errors > 0 {
69            // Pop first so we never go above the capacity and allocate.
70            if self.recent_errors.len() >= self.retained_errors {
71                self.recent_errors.pop_front().unwrap();
72            }
73            self.recent_errors.push_back(error);
74        }
75    }
76
77    pub(crate) fn export(
78        &self,
79        addr: std::net::SocketAddr,
80        proto: crate::config::Protocol,
81    ) -> NameServerStats {
82        let Self { successes, failures, success_streak, retained_errors, recent_errors } = self;
83
84        NameServerStats {
85            addr,
86            proto,
87            failures: *failures,
88            successes: *successes,
89            success_streak: *success_streak,
90            recent_errors: self.recent_errors.iter().cloned().collect(),
91        }
92    }
93}
94
95impl PartialEq for InternalNameServerStats {
96    fn eq(&self, other: &Self) -> bool {
97        self.successes == other.successes && self.failures == other.failures
98    }
99}
100
101impl Eq for InternalNameServerStats {}
102
103impl Ord for InternalNameServerStats {
104    /// Custom implementation of Ord for NameServer which incorporates the performance of the connection into it's ranking
105    fn cmp(&self, other: &Self) -> Ordering {
106        // if they are literally equal, just return
107        if self == other {
108            return Ordering::Equal;
109        }
110
111        // TODO: track latency and use lowest latency connection...
112
113        // invert failure comparison, i.e. the one with the least failures, wins
114        if self.failures <= other.failures {
115            return Ordering::Greater;
116        }
117
118        // at this point we'll go with the lesser of successes to make sure there is balance
119        self.successes.cmp(&other.successes)
120    }
121}
122
123impl PartialOrd for InternalNameServerStats {
124    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
125        Some(self.cmp(other))
126    }
127}
128
129#[cfg(test)]
130mod tests {
131    use super::*;
132
133    fn is_send_sync<S: Sync + Send>() -> bool {
134        true
135    }
136
137    #[test]
138    fn stats_are_sync() {
139        assert!(is_send_sync::<InternalNameServerStats>());
140    }
141
142    #[test]
143    fn test_state_cmp() {
144        let nil = InternalNameServerStats::new_internal(0, 0, 0, 0);
145        let successes = InternalNameServerStats::new_internal(1, 0, 0, 0);
146        let failures = InternalNameServerStats::new_internal(0, 1, 0, 0);
147
148        assert_eq!(nil.cmp(&nil), Ordering::Equal);
149        assert_eq!(nil.cmp(&successes), Ordering::Greater);
150        assert_eq!(successes.cmp(&failures), Ordering::Greater);
151    }
152}