Skip to main content

scion_stack/path/manager/
reliability.rs

1// Copyright 2025 Anapaya Systems
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//   http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use std::time::{Duration, SystemTime};
16
17use crate::path::{manager::algo::exponential_decay, types::Score};
18
19/// Duration after which the reliability score decays to half its value.
20/// After 20 half-lives, the score approaches zero.
21const EXPONENTIAL_DECAY_HALFLIFE: Duration = Duration::from_secs(90); // Full decay in ~30 min
22
23/// Reliability score for a path
24///
25/// A reliability score indicates how reliable a path is, based on reported issues.
26/// The score decays over time, allowing paths to recover over time if no further issues are
27/// reported.
28#[derive(Debug, Clone)]
29pub struct ReliabilityScore {
30    score: f32,
31    last_updated: SystemTime,
32}
33
34impl ReliabilityScore {
35    /// Returns the current reliability score, decayed to `now`.
36    pub fn score(&self, now: SystemTime) -> Score {
37        Score::new_clamped(exponential_decay(
38            self.score,
39            now.duration_since(self.last_updated)
40                .unwrap_or_else(|_| Duration::from_secs(0)),
41            EXPONENTIAL_DECAY_HALFLIFE,
42        ))
43    }
44
45    /// Creates a new ReliabilityScore with initial score of 0.0
46    ///
47    /// `now` is the current time for initialization, used for decay calculations.
48    pub fn new_with_time(now: SystemTime) -> Self {
49        ReliabilityScore {
50            score: 0.0,
51            last_updated: now,
52        }
53    }
54
55    /// Updates the reliability score based on the reported issue.
56    ///
57    /// `penalty` is the penalty score to apply usually a negative value.
58    /// `now` is the current time for decay calculations.
59    pub fn update(&mut self, penalty: Score, now: SystemTime) {
60        let current_score = self.score(now); // Get decayed score
61        let new_score = current_score.value() + penalty.value(); // Apply penalty
62
63        self.score = new_score.clamp(-1000.0, 1000.0); // For sanity, clamp score
64        self.last_updated = now;
65    }
66}