phantom_protocol/transport/
fallback.rs1use crate::transport::scheduler::Scheduler;
18use parking_lot::RwLock;
19use std::sync::atomic::{AtomicU32, AtomicU64, Ordering};
20use std::sync::Arc;
21use std::time::Instant;
22
23#[derive(Debug, Clone, Copy, PartialEq, Eq)]
27pub enum TransportMode {
28 Turbo,
30 Reliable,
32 Stealth,
35}
36
37#[derive(Debug, Clone)]
39pub struct FallbackTrigger {
40 pub max_rtt: u32,
41 pub max_loss: u8,
42 pub failure_threshold: u32,
43}
44
45impl Default for FallbackTrigger {
46 fn default() -> Self {
47 Self {
48 max_rtt: 500,
49 max_loss: 10,
50 failure_threshold: 3,
51 }
52 }
53}
54
55#[derive(Debug, Default)]
56pub struct FallbackMetrics {
57 pub packets_sent: AtomicU64,
58 pub packets_acked: AtomicU64,
59 pub connection_failures: AtomicU32,
60 pub last_success_ms: AtomicU64,
61}
62
63impl FallbackMetrics {
64 pub fn record_sent(&self) {
65 self.packets_sent.fetch_add(1, Ordering::Relaxed);
66 }
67
68 pub fn record_success(&self) {
69 self.packets_acked.fetch_add(1, Ordering::Relaxed);
70 self.connection_failures.store(0, Ordering::Relaxed);
71 let now = std::time::SystemTime::now()
72 .duration_since(std::time::UNIX_EPOCH)
73 .map(|d| d.as_millis() as u64)
74 .unwrap_or_default();
75 self.last_success_ms.store(now, Ordering::Relaxed);
76 }
77
78 pub fn record_failure(&self) {
79 self.connection_failures.fetch_add(1, Ordering::Relaxed);
80 }
81}
82
83pub struct FallbackStateMachine {
85 current_mode: RwLock<TransportMode>,
87 trigger: FallbackTrigger,
89 metrics: FallbackMetrics,
91 last_change: RwLock<Instant>,
93 last_probe: RwLock<Instant>,
95 best_mode: TransportMode,
97 #[allow(dead_code)]
101 scheduler: Option<Arc<Scheduler>>,
102}
103
104impl FallbackStateMachine {
105 pub fn with_defaults() -> Self {
106 Self::new(FallbackTrigger::default())
107 }
108
109 pub fn new(trigger: FallbackTrigger) -> Self {
110 Self {
111 current_mode: RwLock::new(TransportMode::Turbo),
112 best_mode: TransportMode::Turbo,
113 trigger,
114 metrics: FallbackMetrics::default(),
115 last_change: RwLock::new(Instant::now()),
116 last_probe: RwLock::new(Instant::now()),
117 scheduler: None,
118 }
119 }
120
121 pub fn metrics(&self) -> &FallbackMetrics {
122 &self.metrics
123 }
124
125 pub fn current_mode(&self) -> TransportMode {
126 *self.current_mode.read()
127 }
128
129 pub fn check_and_fallback(&self) -> bool {
130 let failures = self.metrics.connection_failures.load(Ordering::Relaxed);
131 if failures >= self.trigger.failure_threshold {
132 self.degrade();
133 return true;
134 }
135 false
136 }
137
138 pub fn record_failure(&self) {
139 self.metrics.record_failure();
140 let _ = self.check_and_fallback();
141 }
142
143 fn degrade(&self) {
144 let mut mode = self.current_mode.write();
145 let new_mode = match *mode {
146 TransportMode::Turbo => TransportMode::Reliable,
147 TransportMode::Reliable => TransportMode::Stealth,
148 TransportMode::Stealth => TransportMode::Stealth,
149 };
150
151 if new_mode != *mode {
152 log::warn!("Transport degradation: {:?} -> {:?}", *mode, new_mode);
153 *mode = new_mode;
154 *self.last_change.write() = Instant::now();
155 }
156 }
157
158 pub fn upgrade(&self) {
159 let mut mode = self.current_mode.write();
160 if *mode != self.best_mode {
161 log::info!("Transport healing: {:?} -> {:?}", *mode, self.best_mode);
162 *mode = self.best_mode;
163 *self.last_change.write() = Instant::now();
164 self.metrics.connection_failures.store(0, Ordering::Relaxed);
166 }
167 }
168
169 pub fn should_probe(&self) -> bool {
170 let mode = self.current_mode.read();
171 if *mode == self.best_mode {
172 return false;
173 }
174
175 let last_probe = self.last_probe.read();
176 let last_change = self.last_change.read();
177
178 last_probe.elapsed() > std::time::Duration::from_secs(30)
180 && last_change.elapsed() > std::time::Duration::from_secs(30)
181 }
182
183 pub fn record_probe(&self) {
184 *self.last_probe.write() = Instant::now();
185 }
186}
187
188#[cfg(test)]
189mod tests {
190 use super::*;
191
192 #[test]
193 fn test_fallback_cycle() {
194 let fsm = FallbackStateMachine::with_defaults();
195 assert_eq!(fsm.current_mode(), TransportMode::Turbo);
196
197 fsm.degrade();
199 assert_eq!(fsm.current_mode(), TransportMode::Reliable);
200
201 fsm.degrade();
203 assert_eq!(fsm.current_mode(), TransportMode::Stealth);
204
205 fsm.upgrade();
207 assert_eq!(fsm.current_mode(), TransportMode::Turbo);
208 }
209
210 #[test]
211 fn test_should_probe() {
212 let fsm = FallbackStateMachine::with_defaults();
213
214 assert!(!fsm.should_probe());
216
217 fsm.degrade();
218 assert_eq!(fsm.current_mode(), TransportMode::Reliable);
219
220 assert!(!fsm.should_probe());
222
223 fsm.record_probe();
226 assert!(!fsm.should_probe());
227 }
228}