1use do_over::{error::DoOverError, hedge::Hedge, policy::Policy};
8use std::sync::atomic::{AtomicUsize, Ordering};
9use std::sync::Arc;
10use std::time::{Duration, Instant};
11
12#[tokio::main]
13async fn main() {
14 println!("=== Do-Over Hedge Policy Example ===\n");
15 println!("Configuration: hedge_delay=100ms");
16 println!("(A backup request is sent 100ms after the primary)\n");
17
18 fast_primary_example().await;
20
21 println!("\n{}\n", "─".repeat(60));
22
23 slow_primary_example().await;
25
26 println!("\n{}\n", "─".repeat(60));
27
28 race_example().await;
30}
31
32async fn fast_primary_example() {
33 println!("📌 Scenario 1: Fast Primary (completes before hedge starts)");
34 println!(" Primary latency: 50ms, Hedge delay: 100ms\n");
35
36 let hedge = Hedge::new(Duration::from_millis(100));
37 let request_count = Arc::new(AtomicUsize::new(0));
38 let start = Instant::now();
39
40 let result: Result<String, DoOverError<String>> = {
41 let rc = Arc::clone(&request_count);
42 hedge
43 .execute(|| {
44 let count = Arc::clone(&rc);
45 async move {
46 let req_num = count.fetch_add(1, Ordering::SeqCst) + 1;
47 let elapsed = start.elapsed().as_millis();
48 println!(
49 " [+{:>4}ms] Request {} started ({})",
50 elapsed,
51 req_num,
52 if req_num == 1 { "primary" } else { "hedge" }
53 );
54
55 tokio::time::sleep(Duration::from_millis(50)).await;
57
58 let elapsed = start.elapsed().as_millis();
59 println!(
60 " [+{:>4}ms] Request {} completed",
61 elapsed, req_num
62 );
63 Ok(format!("Response from request {}", req_num))
64 }
65 })
66 .await
67 };
68
69 let total_time = start.elapsed().as_millis();
70 let total_requests = request_count.load(Ordering::SeqCst);
71
72 println!("\n Result: {:?}", result.unwrap());
73 println!(" Total latency: {}ms", total_time);
74 println!(" Requests made: {}", total_requests);
75 println!("\n 💡 Primary completed in 50ms, before the 100ms hedge delay");
76}
77
78async fn slow_primary_example() {
79 println!("📌 Scenario 2: Slow Primary (hedge wins)");
80 println!(" Primary latency: 300ms, Hedge delay: 100ms, Hedge latency: 50ms\n");
81
82 let hedge = Hedge::new(Duration::from_millis(100));
83 let request_count = Arc::new(AtomicUsize::new(0));
84 let start = Instant::now();
85
86 let result: Result<String, DoOverError<String>> = {
87 let rc = Arc::clone(&request_count);
88 hedge
89 .execute(|| {
90 let count = Arc::clone(&rc);
91 async move {
92 let req_num = count.fetch_add(1, Ordering::SeqCst) + 1;
93 let elapsed = start.elapsed().as_millis();
94 let is_primary = req_num == 1;
95
96 println!(
97 " [+{:>4}ms] Request {} started ({})",
98 elapsed,
99 req_num,
100 if is_primary { "primary" } else { "hedge" }
101 );
102
103 let delay = if is_primary { 300 } else { 50 };
105 tokio::time::sleep(Duration::from_millis(delay)).await;
106
107 let elapsed = start.elapsed().as_millis();
108 println!(
109 " [+{:>4}ms] Request {} completed",
110 elapsed, req_num
111 );
112 Ok(format!("Response from request {}", req_num))
113 }
114 })
115 .await
116 };
117
118 let total_time = start.elapsed().as_millis();
119 let total_requests = request_count.load(Ordering::SeqCst);
120
121 println!("\n Result: {:?}", result.unwrap());
122 println!(" Total latency: {}ms (would be 300ms without hedging)", total_time);
123 println!(" Requests made: {}", total_requests);
124 println!("\n 💡 Hedge started at 100ms, completed at 150ms, beating the slow primary");
125}
126
127async fn race_example() {
128 println!("📌 Scenario 3: Close Race (both requests similar speed)");
129 println!(" Primary latency: 120ms, Hedge delay: 100ms, Hedge latency: 30ms\n");
130
131 let hedge = Hedge::new(Duration::from_millis(100));
132 let request_count = Arc::new(AtomicUsize::new(0));
133 let start = Instant::now();
134
135 let result: Result<String, DoOverError<String>> = {
136 let rc = Arc::clone(&request_count);
137 hedge
138 .execute(|| {
139 let count = Arc::clone(&rc);
140 async move {
141 let req_num = count.fetch_add(1, Ordering::SeqCst) + 1;
142 let elapsed = start.elapsed().as_millis();
143 let is_primary = req_num == 1;
144
145 println!(
146 " [+{:>4}ms] Request {} started ({})",
147 elapsed,
148 req_num,
149 if is_primary { "primary" } else { "hedge" }
150 );
151
152 let delay = if is_primary { 120 } else { 30 };
155 tokio::time::sleep(Duration::from_millis(delay)).await;
156
157 let elapsed = start.elapsed().as_millis();
158 println!(
159 " [+{:>4}ms] Request {} completed",
160 elapsed, req_num
161 );
162 Ok(format!("Response from request {} ({})", req_num, if is_primary { "primary" } else { "hedge" }))
163 }
164 })
165 .await
166 };
167
168 let total_time = start.elapsed().as_millis();
169 let total_requests = request_count.load(Ordering::SeqCst);
170
171 println!("\n Result: {:?}", result.unwrap());
172 println!(" Total latency: {}ms", total_time);
173 println!(" Requests made: {}", total_requests);
174 println!("\n 💡 Primary wins at ~120ms, hedge (would finish at ~130ms) is cancelled");
175}