1use std::sync::Arc;
3use std::sync::atomic::{AtomicUsize, Ordering};
4use std::time::Duration;
5
6use async_trait::async_trait;
7use ecat_circuit_breaker::{Breaker, BreakerConfig, BreakerState};
8
9use crate::breaker::map_breaker_error;
10use crate::dialect::Dialect;
11use crate::rdbms::{RdbmsClient, RdbmsError, Row, SqlExecutor, Transaction};
12
13struct Endpoint {
19 client: Arc<dyn RdbmsClient>,
20 breaker: Breaker,
21}
22
23impl Endpoint {
24 fn new(client: Arc<dyn RdbmsClient>, cfg: &BreakerConfig) -> Self {
25 Self {
26 client,
27 breaker: Breaker::new(cfg.clone()),
28 }
29 }
30
31 async fn is_available(&self, max_lag: Option<Duration>) -> bool {
34 if self.breaker.state() == BreakerState::Open {
35 return false;
36 }
37 match max_lag {
38 Some(max) => self.client.replica_lag().await.is_none_or(|lag| lag <= max),
39 None => true,
40 }
41 }
42
43 fn dialect(&self) -> Dialect {
44 self.client.dialect()
45 }
46
47 async fn execute(&self, sql: &str) -> Result<u64, RdbmsError> {
48 self.breaker
49 .call(|| self.client.execute(sql))
50 .await
51 .map_err(map_breaker_error)
52 }
53
54 async fn execute_with(
55 &self,
56 sql: &str,
57 params: &[serde_json::Value],
58 ) -> Result<u64, RdbmsError> {
59 self.breaker
60 .call(|| self.client.execute_with(sql, params))
61 .await
62 .map_err(map_breaker_error)
63 }
64
65 async fn query(&self, sql: &str) -> Result<Vec<Row>, RdbmsError> {
66 self.breaker
67 .call(|| self.client.query(sql))
68 .await
69 .map_err(map_breaker_error)
70 }
71
72 async fn query_with(
73 &self,
74 sql: &str,
75 params: &[serde_json::Value],
76 ) -> Result<Vec<Row>, RdbmsError> {
77 self.breaker
78 .call(|| self.client.query_with(sql, params))
79 .await
80 .map_err(map_breaker_error)
81 }
82
83 async fn query_write(
84 &self,
85 sql: &str,
86 params: &[serde_json::Value],
87 ) -> Result<Vec<Row>, RdbmsError> {
88 self.breaker
89 .call(|| self.client.query_write(sql, params))
90 .await
91 .map_err(map_breaker_error)
92 }
93
94 async fn execute_then_query(
95 &self,
96 first: &str,
97 first_params: &[serde_json::Value],
98 second: &str,
99 ) -> Result<Vec<Row>, RdbmsError> {
100 self.breaker
101 .call(|| self.client.execute_then_query(first, first_params, second))
102 .await
103 .map_err(map_breaker_error)
104 }
105}
106
107pub struct RdbmsRouting {
112 primary: Endpoint,
113 replicas: Vec<Endpoint>,
114 next: AtomicUsize,
116 fallback_to_primary: bool,
118 max_replica_lag: Option<Duration>,
120}
121
122impl RdbmsRouting {
123 pub fn new(primary: Arc<dyn RdbmsClient>, replicas: Vec<Arc<dyn RdbmsClient>>) -> Self {
125 Self::with_breaker_config(primary, replicas, BreakerConfig::default())
126 }
127
128 pub fn with_breaker_config(
130 primary: Arc<dyn RdbmsClient>,
131 replicas: Vec<Arc<dyn RdbmsClient>>,
132 cfg: BreakerConfig,
133 ) -> Self {
134 Self {
135 primary: Endpoint::new(primary, &cfg),
136 replicas: replicas
137 .into_iter()
138 .map(|client| Endpoint::new(client, &cfg))
139 .collect(),
140 next: AtomicUsize::new(0),
141 fallback_to_primary: true,
142 max_replica_lag: None,
143 }
144 }
145
146 pub fn fallback_to_primary(mut self, yes: bool) -> Self {
149 self.fallback_to_primary = yes;
150 self
151 }
152
153 pub fn max_replica_lag(mut self, lag: Duration) -> Self {
156 self.max_replica_lag = Some(lag);
157 self
158 }
159
160 async fn pick_replica(&self) -> Option<&Endpoint> {
168 let n = self.replicas.len();
169 if n == 0 {
170 return None;
171 }
172 let start = self.next.fetch_add(1, Ordering::Relaxed);
173 for i in 0..n {
174 let ep = &self.replicas[(start + i) % n];
175 if ep.is_available(self.max_replica_lag).await {
176 return Some(ep);
177 }
178 }
179 None
180 }
181}
182
183#[async_trait]
184impl SqlExecutor for RdbmsRouting {
185 async fn execute(&self, sql: &str) -> Result<u64, RdbmsError> {
187 self.primary.execute(sql).await
188 }
189
190 async fn query(&self, sql: &str) -> Result<Vec<Row>, RdbmsError> {
192 match self.pick_replica().await {
193 Some(replica) => replica.query(sql).await,
194 None if self.fallback_to_primary => self.primary.query(sql).await,
195 None => Err(RdbmsError::NoAvailableReplica),
196 }
197 }
198
199 async fn execute_with(
201 &self,
202 sql: &str,
203 params: &[serde_json::Value],
204 ) -> Result<u64, RdbmsError> {
205 self.primary.execute_with(sql, params).await
206 }
207
208 async fn query_with(
210 &self,
211 sql: &str,
212 params: &[serde_json::Value],
213 ) -> Result<Vec<Row>, RdbmsError> {
214 match self.pick_replica().await {
215 Some(replica) => replica.query_with(sql, params).await,
216 None if self.fallback_to_primary => self.primary.query_with(sql, params).await,
217 None => Err(RdbmsError::NoAvailableReplica),
218 }
219 }
220
221 async fn query_write(
224 &self,
225 sql: &str,
226 params: &[serde_json::Value],
227 ) -> Result<Vec<Row>, RdbmsError> {
228 self.primary.query_write(sql, params).await
229 }
230
231 async fn execute_then_query(
233 &self,
234 first: &str,
235 first_params: &[serde_json::Value],
236 second: &str,
237 ) -> Result<Vec<Row>, RdbmsError> {
238 self.primary
239 .execute_then_query(first, first_params, second)
240 .await
241 }
242
243 fn dialect(&self) -> Dialect {
245 self.primary.dialect()
246 }
247}
248
249#[async_trait]
250impl RdbmsClient for RdbmsRouting {
251 async fn transaction(&self) -> Result<Transaction, RdbmsError> {
254 self.primary.client.transaction().await
255 }
256}
257
258pub struct PrimaryOnly<'a> {
263 router: &'a RdbmsRouting,
264}
265
266impl RdbmsRouting {
267 pub fn primary_only(&self) -> PrimaryOnly<'_> {
270 PrimaryOnly { router: self }
271 }
272}
273
274#[async_trait]
275impl SqlExecutor for PrimaryOnly<'_> {
276 async fn execute(&self, sql: &str) -> Result<u64, RdbmsError> {
277 self.router.primary.execute(sql).await
278 }
279
280 async fn query(&self, sql: &str) -> Result<Vec<Row>, RdbmsError> {
281 self.router.primary.query(sql).await
282 }
283
284 async fn execute_with(
285 &self,
286 sql: &str,
287 params: &[serde_json::Value],
288 ) -> Result<u64, RdbmsError> {
289 self.router.primary.execute_with(sql, params).await
290 }
291
292 async fn query_with(
293 &self,
294 sql: &str,
295 params: &[serde_json::Value],
296 ) -> Result<Vec<Row>, RdbmsError> {
297 self.router.primary.query_with(sql, params).await
298 }
299
300 async fn query_write(
301 &self,
302 sql: &str,
303 params: &[serde_json::Value],
304 ) -> Result<Vec<Row>, RdbmsError> {
305 self.router.primary.query_write(sql, params).await
306 }
307
308 async fn execute_then_query(
309 &self,
310 first: &str,
311 first_params: &[serde_json::Value],
312 second: &str,
313 ) -> Result<Vec<Row>, RdbmsError> {
314 self.router
315 .primary
316 .execute_then_query(first, first_params, second)
317 .await
318 }
319
320 fn dialect(&self) -> Dialect {
321 self.router.primary.dialect()
322 }
323}
324
325#[async_trait]
326impl RdbmsClient for PrimaryOnly<'_> {
327 async fn transaction(&self) -> Result<Transaction, RdbmsError> {
329 self.router.primary.client.transaction().await
330 }
331}
332
333#[cfg(test)]
334mod tests;