go_server_rust_sdk/scheduler/retry_client.rs
1//! Retry client implementation for the scheduler
2
3use serde_json::Value;
4use std::time::Duration;
5use tokio::time::sleep;
6use crate::error::{Result, SdkError};
7use super::client::{Client, ResultResponse};
8
9/// Retry client that wraps the base client with retry functionality
10#[derive(Clone)]
11pub struct RetryClient {
12 client: Client,
13 max_retries: usize,
14 retry_delay: Duration,
15}
16
17impl RetryClient {
18 /// Creates a new retry client
19 ///
20 /// # Arguments
21 ///
22 /// * `base_url` - The base URL of the scheduler server
23 /// * `max_retries` - Maximum number of retry attempts
24 /// * `retry_delay` - Delay between retry attempts
25 ///
26 /// # Example
27 ///
28 /// ```rust
29 /// use go_server_rust_sdk::scheduler::RetryClient;
30 /// use std::time::Duration;
31 ///
32 /// let client = RetryClient::new(
33 /// "http://localhost:8080",
34 /// 3,
35 /// Duration::from_secs(1)
36 /// );
37 /// ```
38 pub fn new(
39 base_url: impl Into<String>,
40 max_retries: usize,
41 retry_delay: Duration,
42 ) -> Self {
43 Self {
44 client: Client::new(base_url),
45 max_retries,
46 retry_delay,
47 }
48 }
49
50 /// Executes a task with retry logic
51 ///
52 /// This method will retry the execution up to `max_retries` times
53 /// if the request fails.
54 ///
55 /// # Arguments
56 ///
57 /// * `method` - The method name to execute
58 /// * `params` - The parameters for the method
59 ///
60 /// # Returns
61 ///
62 /// A `ResultResponse` containing the task ID and initial status
63 ///
64 /// # Example
65 ///
66 /// ```rust,no_run
67 /// use go_server_rust_sdk::scheduler::RetryClient;
68 /// use serde_json::json;
69 /// use std::time::Duration;
70 ///
71 /// # #[tokio::main]
72 /// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
73 /// let client = RetryClient::new(
74 /// "http://localhost:8080",
75 /// 3,
76 /// Duration::from_secs(1)
77 /// );
78 /// let params = json!({"a": 10, "b": 20});
79 /// let response = client.execute_with_retry("add", params).await?;
80 /// println!("Task ID: {}", response.task_id);
81 /// # Ok(())
82 /// # }
83 /// ```
84 pub async fn execute_with_retry(
85 &self,
86 method: impl Into<String> + Clone,
87 params: Value,
88 ) -> Result<ResultResponse> {
89 let mut last_error = None;
90
91 for attempt in 0..=self.max_retries {
92 match self.client.execute(method.clone(), params.clone()).await {
93 Ok(response) => return Ok(response),
94 Err(e) => {
95 last_error = Some(e);
96 if attempt < self.max_retries {
97 sleep(self.retry_delay).await;
98 }
99 }
100 }
101 }
102
103 Err(SdkError::Generic(format!(
104 "Failed after {} retries: {}",
105 self.max_retries,
106 last_error.unwrap()
107 )))
108 }
109
110 /// Executes an encrypted task with retry logic
111 ///
112 /// This method will retry the execution up to `max_retries` times
113 /// if the request fails.
114 ///
115 /// # Arguments
116 ///
117 /// * `method` - The method name to execute
118 /// * `key` - The encryption key
119 /// * `salt` - The salt value for key encryption
120 /// * `params` - The parameters for the method
121 ///
122 /// # Returns
123 ///
124 /// A `ResultResponse` containing the task ID and initial status
125 ///
126 /// # Example
127 ///
128 /// ```rust,no_run
129 /// use go_server_rust_sdk::scheduler::RetryClient;
130 /// use serde_json::json;
131 /// use std::time::Duration;
132 ///
133 /// # #[tokio::main]
134 /// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
135 /// let client = RetryClient::new(
136 /// "http://localhost:8080",
137 /// 3,
138 /// Duration::from_secs(1)
139 /// );
140 /// let params = json!({"a": 10, "b": 20});
141 /// let response = client.execute_encrypted_with_retry(
142 /// "add",
143 /// "my-secret-key",
144 /// 123456,
145 /// params
146 /// ).await?;
147 /// println!("Task ID: {}", response.task_id);
148 /// # Ok(())
149 /// # }
150 /// ```
151 pub async fn execute_encrypted_with_retry(
152 &self,
153 method: impl Into<String> + Clone,
154 key: &str,
155 salt: i32,
156 params: Value,
157 ) -> Result<ResultResponse> {
158 let mut last_error = None;
159
160 for attempt in 0..=self.max_retries {
161 match self.client.execute_encrypted(method.clone(), key, salt, params.clone()).await {
162 Ok(response) => return Ok(response),
163 Err(e) => {
164 last_error = Some(e);
165 if attempt < self.max_retries {
166 sleep(self.retry_delay).await;
167 }
168 }
169 }
170 }
171
172 Err(SdkError::Generic(format!(
173 "Failed after {} retries: {}",
174 self.max_retries,
175 last_error.unwrap()
176 )))
177 }
178
179 /// Executes a task synchronously with retry logic and timeout
180 ///
181 /// This is a convenience method that combines retry logic with synchronous execution.
182 ///
183 /// # Arguments
184 ///
185 /// * `method` - The method name to execute
186 /// * `params` - The parameters for the method
187 /// * `timeout_duration` - Maximum time to wait for completion
188 ///
189 /// # Returns
190 ///
191 /// A `ResultResponse` containing the final task result
192 ///
193 /// # Example
194 ///
195 /// ```rust,no_run
196 /// use go_server_rust_sdk::scheduler::RetryClient;
197 /// use serde_json::json;
198 /// use std::time::Duration;
199 ///
200 /// # #[tokio::main]
201 /// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
202 /// let client = RetryClient::new(
203 /// "http://localhost:8080",
204 /// 3,
205 /// Duration::from_secs(1)
206 /// );
207 /// let params = json!({"a": 10, "b": 20});
208 /// let result = client.execute_sync_with_retry(
209 /// "add",
210 /// params,
211 /// Duration::from_secs(30)
212 /// ).await?;
213 /// println!("Result: {:?}", result.result);
214 /// # Ok(())
215 /// # }
216 /// ```
217 pub async fn execute_sync_with_retry(
218 &self,
219 method: impl Into<String> + Clone,
220 params: Value,
221 _timeout_duration: Duration,
222 ) -> Result<ResultResponse> {
223 // Submit task with retry
224 let exec_response = self.execute_with_retry(method, params).await?;
225
226 // Get result (this already has built-in polling)
227 let result = self.client.get_result(&exec_response.task_id).await?;
228
229 Ok(result)
230 }
231
232 /// Executes an encrypted task synchronously with retry logic, decryption and timeout
233 ///
234 /// This is a convenience method that combines retry logic with synchronous encrypted execution.
235 ///
236 /// # Arguments
237 ///
238 /// * `method` - The method name to execute
239 /// * `key` - The encryption key
240 /// * `salt` - The salt value for key encryption
241 /// * `params` - The parameters for the method
242 /// * `timeout_duration` - Maximum time to wait for completion
243 ///
244 /// # Returns
245 ///
246 /// A `ResultResponse` containing the decrypted task result
247 ///
248 /// # Example
249 ///
250 /// ```rust,no_run
251 /// use go_server_rust_sdk::scheduler::RetryClient;
252 /// use serde_json::json;
253 /// use std::time::Duration;
254 ///
255 /// # #[tokio::main]
256 /// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
257 /// let client = RetryClient::new(
258 /// "http://localhost:8080",
259 /// 3,
260 /// Duration::from_secs(1)
261 /// );
262 /// let params = json!({"a": 10, "b": 20});
263 /// let result = client.execute_sync_encrypted_with_retry(
264 /// "add",
265 /// "my-secret-key",
266 /// 123456,
267 /// params,
268 /// Duration::from_secs(30)
269 /// ).await?;
270 /// println!("Decrypted result: {:?}", result.result);
271 /// # Ok(())
272 /// # }
273 /// ```
274 pub async fn execute_sync_encrypted_with_retry(
275 &self,
276 method: impl Into<String> + Clone,
277 key: &str,
278 salt: i32,
279 params: Value,
280 _timeout_duration: Duration,
281 ) -> Result<ResultResponse> {
282 // Submit encrypted task with retry
283 let exec_response = self.execute_encrypted_with_retry(method, key, salt, params).await?;
284
285 // Get and decrypt result (this already has built-in polling)
286 let result = self.client.get_result_encrypted(&exec_response.task_id, key, salt).await?;
287
288 Ok(result)
289 }
290
291 /// Get access to the underlying client
292 ///
293 /// This allows access to methods that don't need retry logic,
294 /// such as `get_result` and `get_result_encrypted`.
295 ///
296 /// # Returns
297 ///
298 /// A reference to the underlying `Client`
299 pub fn client(&self) -> &Client {
300 &self.client
301 }
302}
303
304#[cfg(test)]
305mod tests {
306 use super::*;
307 use std::time::Duration;
308
309 #[test]
310 fn test_retry_client_creation() {
311 let client = RetryClient::new(
312 "http://localhost:8080",
313 3,
314 Duration::from_millis(500),
315 );
316
317 assert_eq!(client.max_retries, 3);
318 assert_eq!(client.retry_delay, Duration::from_millis(500));
319 }
320
321 #[test]
322 fn test_client_access() {
323 let retry_client = RetryClient::new(
324 "http://localhost:8080",
325 3,
326 Duration::from_millis(500),
327 );
328
329 let _client = retry_client.client();
330 // Should be able to access the underlying client
331 }
332}