linkrs_query/optimizer/stats/feedback/
trigger.rs1use std::sync::atomic::{AtomicU64, Ordering};
7use std::time::Instant;
8
9#[derive(Debug, Clone)]
22pub struct AutoFeedbackConfig {
23 pub min_samples_for_update: usize,
25 pub error_threshold: f64,
27 pub update_cooldown_ms: u64,
29 pub max_feedback_history: usize,
31 pub enabled: bool,
33}
34
35impl AutoFeedbackConfig {
36 pub fn new() -> Self {
38 Self {
39 min_samples_for_update: 10,
40 error_threshold: 0.5,
41 update_cooldown_ms: 60000, max_feedback_history: 100,
43 enabled: true,
44 }
45 }
46
47 pub fn with_params(
49 min_samples: usize,
50 error_threshold: f64,
51 cooldown_ms: u64,
52 max_history: usize,
53 ) -> Self {
54 Self {
55 min_samples_for_update: min_samples,
56 error_threshold: error_threshold.clamp(0.1, 1.0),
57 update_cooldown_ms: cooldown_ms,
58 max_feedback_history: max_history,
59 enabled: true,
60 }
61 }
62
63 pub fn should_trigger_update(
74 &self,
75 feedback_count: usize,
76 last_update_ms: u64,
77 current_error: f64,
78 ) -> bool {
79 if !self.enabled {
80 return false;
81 }
82
83 if current_error > self.error_threshold {
85 return true;
86 }
87
88 if feedback_count < self.min_samples_for_update {
90 return false;
91 }
92
93 let current_time = Instant::now().elapsed().as_millis() as u64;
95 if current_time.saturating_sub(last_update_ms) < self.update_cooldown_ms {
96 return false;
97 }
98
99 true
100 }
101
102 pub fn enable(&mut self) {
104 self.enabled = true;
105 }
106
107 pub fn disable(&mut self) {
109 self.enabled = false;
110 }
111}
112
113impl Default for AutoFeedbackConfig {
114 fn default() -> Self {
115 Self::new()
116 }
117}
118
119#[derive(Debug)]
140pub struct AutoFeedbackTrigger {
141 config: AutoFeedbackConfig,
143 last_update_time_ms: AtomicU64,
145 feedback_count: AtomicU64,
147}
148
149impl AutoFeedbackTrigger {
150 pub fn new(config: AutoFeedbackConfig) -> Self {
152 Self {
153 config,
154 last_update_time_ms: AtomicU64::new(0),
155 feedback_count: AtomicU64::new(0),
156 }
157 }
158
159 pub fn record_feedback(&self) {
161 self.feedback_count.fetch_add(1, Ordering::Relaxed);
162 }
163
164 pub fn should_trigger(&self, current_error: f64) -> bool {
166 let count = self.feedback_count.load(Ordering::Relaxed) as usize;
167 let last_update = self.last_update_time_ms.load(Ordering::Relaxed);
168 self.config
169 .should_trigger_update(count, last_update, current_error)
170 }
171
172 pub fn mark_updated(&self) {
174 let current_time = Instant::now().elapsed().as_millis() as u64;
175 self.last_update_time_ms
176 .store(current_time, Ordering::Relaxed);
177 self.feedback_count.store(0, Ordering::Relaxed);
178 }
179
180 pub fn get_feedback_count(&self) -> u64 {
182 self.feedback_count.load(Ordering::Relaxed)
183 }
184
185 pub fn update_config(&mut self, config: AutoFeedbackConfig) {
187 self.config = config;
188 }
189
190 pub fn config(&self) -> &AutoFeedbackConfig {
192 &self.config
193 }
194}
195
196impl Default for AutoFeedbackTrigger {
197 fn default() -> Self {
198 Self::new(AutoFeedbackConfig::default())
199 }
200}
201
202#[cfg(test)]
203mod tests {
204 use super::*;
205
206 #[test]
207 fn test_auto_feedback_config() {
208 let config = AutoFeedbackConfig::new();
209 assert!(config.enabled);
210 assert_eq!(config.min_samples_for_update, 10);
211
212 assert!(!config.should_trigger_update(5, 0, 0.1)); assert!(config.should_trigger_update(5, 0, 0.6)); let current_time = Instant::now().elapsed().as_millis() as u64;
218 if current_time > config.update_cooldown_ms {
219 assert!(config.should_trigger_update(15, 0, 0.1)); }
221 }
222
223 #[test]
224 fn test_auto_feedback_trigger() {
225 let config = AutoFeedbackConfig::with_params(5, 0.5, 1000, 50);
226 let trigger = AutoFeedbackTrigger::new(config);
227
228 assert!(!trigger.should_trigger(0.1)); for _ in 0..5 {
232 trigger.record_feedback();
233 }
234
235 assert!(trigger.should_trigger(0.6));
237
238 trigger.mark_updated();
239 assert_eq!(trigger.get_feedback_count(), 0);
240 }
241
242 #[test]
243 fn test_config_enable_disable() {
244 let mut config = AutoFeedbackConfig::new();
245 assert!(config.enabled);
246
247 config.disable();
248 assert!(!config.enabled);
249 assert!(!config.should_trigger_update(100, 0, 1.0));
250
251 config.enable();
252 assert!(config.enabled);
253 }
254}