ci_engine/result_cache/
spot_check.rs1use super::key::{CacheKey, entry_id_bytes};
5
6#[derive(Debug, Clone, Copy, PartialEq, Eq)]
12pub enum SpotCheck {
13 Never,
15 Always,
17 Fraction {
19 numerator: u32,
21 denominator: u32,
23 },
24}
25
26impl Default for SpotCheck {
27 fn default() -> Self {
28 Self::Fraction {
29 numerator: 1,
30 denominator: 32,
31 }
32 }
33}
34
35impl SpotCheck {
36 #[must_use]
38 pub fn should_sample(&self, key: &CacheKey, check_name: &str) -> bool {
39 match *self {
40 Self::Never => false,
41 Self::Always => true,
42 Self::Fraction {
43 numerator,
44 denominator,
45 } => {
46 if denominator == 0 || numerator == 0 {
47 return false;
48 }
49 if numerator >= denominator {
50 return true;
51 }
52 let bytes = entry_id_bytes(key, check_name);
53 let bucket = u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]);
54 bucket % denominator < numerator
55 }
56 }
57 }
58}
59
60#[cfg(test)]
61mod tests {
62 use crypto::{Basis, BasisKind, StateRef};
63
64 use super::*;
65 use crate::model::ExecutionContext;
66
67 fn key() -> CacheKey {
68 CacheKey::derive(
69 &std::collections::BTreeMap::new(),
70 &ExecutionContext {
71 repo: "test/repo".to_string(),
72 state: StateRef {
73 content_hash: "state".to_string(),
74 change_id: "change".to_string(),
75 logical_change_id: None,
76 },
77 basis: Basis {
78 kind: BasisKind::Branch,
79 evaluated_tree_digest: "tree".to_string(),
80 },
81 definition_digest: "definition".to_string(),
82 toolchain: None,
83 pick_id: None,
84 attempt: 1,
85 runner: None,
86 image_digest: None,
87 },
88 &ci_config::Check {
89 name: "build".to_string(),
90 class: ci_config::CheckClass::Required,
91 command: vec!["true".to_string()],
92 timeout_secs: 1,
93 env: std::collections::BTreeMap::new(),
94 services: Vec::new(),
95 cache_paths: Vec::new(),
96 retry: ci_config::Retry::default(),
97 triggers: Vec::new(),
98 supersede: false,
99 isolation: None,
100 },
101 )
102 }
103
104 #[test]
105 fn fraction_is_deterministic_for_a_key() {
106 let key = key();
107 let policy = SpotCheck::Fraction {
108 numerator: 1,
109 denominator: 2,
110 };
111 let first = policy.should_sample(&key, "build");
112 assert_eq!(first, policy.should_sample(&key, "build"));
113 assert!(SpotCheck::Always.should_sample(&key, "build"));
114 assert!(!SpotCheck::Never.should_sample(&key, "build"));
115 }
116}