1use std::collections::HashMap;
5use std::sync::Arc;
6use std::sync::atomic::{AtomicU64, Ordering};
7
8use std::time::Duration;
9
10use async_trait::async_trait;
11use futures::future::Either;
12use parking_lot::Mutex;
13use thiserror::Error;
14use tokio::sync::oneshot;
15
16use slim_datapath::api::{ProtoMessage as Message, ProtoName, ProtoSubscriptionAck};
17use slim_datapath::messages::utils::SlimHeaderFlags;
18
19use crate::common::SlimChannelSender;
20
21const ACK_TIMEOUT: Duration = Duration::from_secs(10);
29
30#[derive(Error, Debug)]
31pub enum SubscriptionAckError {
32 #[error("ack rejected by datapath: {message}")]
33 Rejected { message: String },
34 #[error("ack channel closed")]
35 ChannelClosed,
36 #[error("ack timed out")]
37 Timeout,
38}
39
40#[async_trait]
47pub trait SubscriptionOps: Clone + Send + Sync + 'static {
48 async fn subscribe(
51 &self,
52 source: &ProtoName,
53 name: &ProtoName,
54 forward_to: Option<u64>,
55 ) -> Result<(u64, oneshot::Receiver<Result<(), SubscriptionAckError>>), SubscriptionAckError>;
56
57 async fn unsubscribe(
59 &self,
60 source: &ProtoName,
61 name: &ProtoName,
62 subscription_id: u64,
63 forward_to: Option<u64>,
64 ) -> Result<oneshot::Receiver<Result<(), SubscriptionAckError>>, SubscriptionAckError>;
65
66 async fn set_route(
68 &self,
69 source: &ProtoName,
70 name: &ProtoName,
71 conn: u64,
72 ) -> Result<(u64, oneshot::Receiver<Result<(), SubscriptionAckError>>), SubscriptionAckError>;
73
74 async fn remove_route(
76 &self,
77 source: &ProtoName,
78 name: &ProtoName,
79 subscription_id: u64,
80 conn: u64,
81 ) -> Result<oneshot::Receiver<Result<(), SubscriptionAckError>>, SubscriptionAckError>;
82
83 fn from_slim_tx(_tx: &SlimChannelSender) -> Option<Self>
88 where
89 Self: Sized,
90 {
91 None
92 }
93}
94
95#[derive(Clone)]
99pub struct AutoAckManager {
100 ack_counter: Arc<AtomicU64>,
101}
102
103#[async_trait]
104impl SubscriptionOps for AutoAckManager {
105 async fn subscribe(
106 &self,
107 _source: &ProtoName,
108 _name: &ProtoName,
109 _forward_to: Option<u64>,
110 ) -> Result<(u64, oneshot::Receiver<Result<(), SubscriptionAckError>>), SubscriptionAckError>
111 {
112 let id = self.ack_counter.fetch_add(1, Ordering::Relaxed) + 1;
113 let (tx, rx) = oneshot::channel();
114 let _ = tx.send(Ok(()));
115 Ok((id, rx))
116 }
117
118 async fn unsubscribe(
119 &self,
120 _source: &ProtoName,
121 _name: &ProtoName,
122 _subscription_id: u64,
123 _forward_to: Option<u64>,
124 ) -> Result<oneshot::Receiver<Result<(), SubscriptionAckError>>, SubscriptionAckError> {
125 let (tx, rx) = oneshot::channel();
126 let _ = tx.send(Ok(()));
127 Ok(rx)
128 }
129
130 async fn set_route(
131 &self,
132 _source: &ProtoName,
133 _name: &ProtoName,
134 _conn: u64,
135 ) -> Result<(u64, oneshot::Receiver<Result<(), SubscriptionAckError>>), SubscriptionAckError>
136 {
137 let id = self.ack_counter.fetch_add(1, Ordering::Relaxed) + 1;
138 let (tx, rx) = oneshot::channel();
139 let _ = tx.send(Ok(()));
140 Ok((id, rx))
141 }
142
143 async fn remove_route(
144 &self,
145 _source: &ProtoName,
146 _name: &ProtoName,
147 _subscription_id: u64,
148 _conn: u64,
149 ) -> Result<oneshot::Receiver<Result<(), SubscriptionAckError>>, SubscriptionAckError> {
150 let (tx, rx) = oneshot::channel();
151 let _ = tx.send(Ok(()));
152 Ok(rx)
153 }
154
155 fn from_slim_tx(_tx: &SlimChannelSender) -> Option<Self> {
156 Some(AutoAckManager {
157 ack_counter: Arc::new(AtomicU64::new(0)),
158 })
159 }
160}
161
162#[derive(Clone)]
163pub struct SubscriptionManager {
164 pub pending_acks: Arc<Mutex<HashMap<u64, oneshot::Sender<Result<(), SubscriptionAckError>>>>>,
165 ack_counter: Arc<AtomicU64>,
166 tx: SlimChannelSender,
167}
168
169#[async_trait]
170impl SubscriptionOps for SubscriptionManager {
171 async fn subscribe(
172 &self,
173 source: &ProtoName,
174 name: &ProtoName,
175 forward_to: Option<u64>,
176 ) -> Result<(u64, oneshot::Receiver<Result<(), SubscriptionAckError>>), SubscriptionAckError>
177 {
178 let source = source.clone();
179 let name = name.clone();
180 self.send_with_receiver(move |ack_id| {
181 let flags = if let Some(conn) = forward_to {
182 SlimHeaderFlags::default().with_forward_to(conn)
183 } else {
184 SlimHeaderFlags::default()
185 };
186 Message::builder()
187 .source(source)
188 .destination(name)
189 .flags(flags)
190 .subscription_id(ack_id)
191 .build_subscribe()
192 .unwrap()
193 })
194 .await
195 }
196
197 async fn unsubscribe(
198 &self,
199 source: &ProtoName,
200 name: &ProtoName,
201 subscription_id: u64,
202 forward_to: Option<u64>,
203 ) -> Result<oneshot::Receiver<Result<(), SubscriptionAckError>>, SubscriptionAckError> {
204 let source = source.clone();
205 let name = name.clone();
206 self.send_with_id(subscription_id, move |ack_id| {
207 let flags = if let Some(conn) = forward_to {
208 SlimHeaderFlags::default().with_forward_to(conn)
209 } else {
210 SlimHeaderFlags::default()
211 };
212 Message::builder()
213 .source(source)
214 .destination(name)
215 .flags(flags)
216 .subscription_id(ack_id)
217 .build_unsubscribe()
218 .unwrap()
219 })
220 .await
221 }
222
223 async fn set_route(
224 &self,
225 source: &ProtoName,
226 name: &ProtoName,
227 conn: u64,
228 ) -> Result<(u64, oneshot::Receiver<Result<(), SubscriptionAckError>>), SubscriptionAckError>
229 {
230 let source = source.clone();
231 let name = name.clone();
232 self.send_with_receiver(move |ack_id| {
233 Message::builder()
234 .source(source)
235 .destination(name)
236 .flags(SlimHeaderFlags::default().with_recv_from(conn))
237 .subscription_id(ack_id)
238 .build_subscribe()
239 .unwrap()
240 })
241 .await
242 }
243
244 async fn remove_route(
245 &self,
246 source: &ProtoName,
247 name: &ProtoName,
248 subscription_id: u64,
249 conn: u64,
250 ) -> Result<oneshot::Receiver<Result<(), SubscriptionAckError>>, SubscriptionAckError> {
251 let source = source.clone();
252 let name = name.clone();
253 self.send_with_id(subscription_id, move |ack_id| {
254 Message::builder()
255 .source(source)
256 .destination(name)
257 .flags(SlimHeaderFlags::default().with_recv_from(conn))
258 .subscription_id(ack_id)
259 .build_unsubscribe()
260 .unwrap()
261 })
262 .await
263 }
264
265 fn from_slim_tx(tx: &SlimChannelSender) -> Option<Self> {
266 Some(SubscriptionManager::new(tx.clone()))
267 }
268}
269
270#[cfg(test)]
273#[derive(Clone)]
274pub struct SpySubscriptionManager {
275 tx: Arc<tokio::sync::mpsc::UnboundedSender<SubscriptionCall>>,
276}
277
278#[cfg(test)]
280#[derive(Debug, Clone, PartialEq)]
281pub enum SubscriptionCall {
282 Subscribe,
283 Unsubscribe,
284 SetRoute,
285 RemoveRoute,
286}
287
288#[cfg(test)]
289impl SpySubscriptionManager {
290 pub fn new() -> (Self, tokio::sync::mpsc::UnboundedReceiver<SubscriptionCall>) {
291 let (tx, rx) = tokio::sync::mpsc::unbounded_channel();
292 (Self { tx: Arc::new(tx) }, rx)
293 }
294}
295
296#[cfg(test)]
297#[async_trait]
298impl SubscriptionOps for SpySubscriptionManager {
299 async fn subscribe(
300 &self,
301 _source: &ProtoName,
302 _name: &ProtoName,
303 _forward_to: Option<u64>,
304 ) -> Result<(u64, oneshot::Receiver<Result<(), SubscriptionAckError>>), SubscriptionAckError>
305 {
306 let _ = self.tx.send(SubscriptionCall::Subscribe);
307 let (tx, rx) = oneshot::channel();
308 let _ = tx.send(Ok(()));
309 Ok((0, rx))
310 }
311
312 async fn unsubscribe(
313 &self,
314 _source: &ProtoName,
315 _name: &ProtoName,
316 _subscription_id: u64,
317 _forward_to: Option<u64>,
318 ) -> Result<oneshot::Receiver<Result<(), SubscriptionAckError>>, SubscriptionAckError> {
319 let _ = self.tx.send(SubscriptionCall::Unsubscribe);
320 let (tx, rx) = oneshot::channel();
321 let _ = tx.send(Ok(()));
322 Ok(rx)
323 }
324
325 async fn set_route(
326 &self,
327 _source: &ProtoName,
328 _name: &ProtoName,
329 _conn: u64,
330 ) -> Result<(u64, oneshot::Receiver<Result<(), SubscriptionAckError>>), SubscriptionAckError>
331 {
332 let _ = self.tx.send(SubscriptionCall::SetRoute);
333 let (tx, rx) = oneshot::channel();
334 let _ = tx.send(Ok(()));
335 Ok((0, rx))
336 }
337
338 async fn remove_route(
339 &self,
340 _source: &ProtoName,
341 _name: &ProtoName,
342 _subscription_id: u64,
343 _conn: u64,
344 ) -> Result<oneshot::Receiver<Result<(), SubscriptionAckError>>, SubscriptionAckError> {
345 let _ = self.tx.send(SubscriptionCall::RemoveRoute);
346 let (tx, rx) = oneshot::channel();
347 let _ = tx.send(Ok(()));
348 Ok(rx)
349 }
350
351 fn from_slim_tx(_tx: &SlimChannelSender) -> Option<Self> {
352 None
353 }
354}
355
356impl SubscriptionManager {
357 pub fn new(tx: SlimChannelSender) -> Self {
358 Self {
359 pending_acks: Arc::new(Mutex::new(HashMap::new())),
360 ack_counter: Arc::new(AtomicU64::new(rand::random::<u64>())),
361 tx,
362 }
363 }
364
365 fn next_ack_id(&self) -> u64 {
366 self.ack_counter.fetch_add(1, Ordering::Relaxed) + 1
367 }
368
369 async fn send_with_receiver(
370 &self,
371 build_message: impl FnOnce(u64) -> Message,
372 ) -> Result<(u64, oneshot::Receiver<Result<(), SubscriptionAckError>>), SubscriptionAckError>
373 {
374 let ack_id = self.next_ack_id();
375 let (ack_tx, ack_rx) = oneshot::channel();
376 {
377 let mut pending = self.pending_acks.lock();
378 pending.insert(ack_id, ack_tx);
379 }
380
381 let msg = build_message(ack_id);
382
383 if self.tx.send(Ok(msg)).await.is_err() {
384 self.pending_acks.lock().remove(&ack_id);
385 return Err(SubscriptionAckError::ChannelClosed);
386 }
387
388 Ok((ack_id, ack_rx))
389 }
390
391 async fn send_with_id(
392 &self,
393 subscription_id: u64,
394 build_message: impl FnOnce(u64) -> Message,
395 ) -> Result<oneshot::Receiver<Result<(), SubscriptionAckError>>, SubscriptionAckError> {
396 let ack_rx = self.register_ack_with_id(subscription_id);
397
398 let msg = build_message(subscription_id);
399
400 if self.tx.send(Ok(msg)).await.is_err() {
401 self.pending_acks.lock().remove(&subscription_id);
402 return Err(SubscriptionAckError::ChannelClosed);
403 }
404
405 Ok(ack_rx)
406 }
407
408 pub fn register_ack(&self) -> (u64, oneshot::Receiver<Result<(), SubscriptionAckError>>) {
412 let ack_id = self.next_ack_id();
413 let (ack_tx, ack_rx) = oneshot::channel();
414 {
415 let mut pending = self.pending_acks.lock();
416 pending.insert(ack_id, ack_tx);
417 }
418 (ack_id, ack_rx)
419 }
420
421 pub fn register_ack_with_id(
423 &self,
424 id: u64,
425 ) -> oneshot::Receiver<Result<(), SubscriptionAckError>> {
426 let (ack_tx, ack_rx) = oneshot::channel();
427 self.pending_acks.lock().insert(id, ack_tx);
428 ack_rx
429 }
430
431 pub fn cancel_ack(&self, ack_id: u64) {
433 let mut pending = self.pending_acks.lock();
434 pending.remove(&ack_id);
435 }
436
437 pub async fn await_ack(
443 ack_rx: oneshot::Receiver<Result<(), SubscriptionAckError>>,
444 ) -> Result<(), SubscriptionAckError> {
445 futures::pin_mut!(ack_rx);
446 let delay = crate::runtime::ack_timeout_delay(ACK_TIMEOUT);
447 futures::pin_mut!(delay);
448
449 match futures::future::select(ack_rx, delay).await {
450 Either::Left((Ok(result), _)) => result,
451 Either::Left((Err(_), _)) => Err(SubscriptionAckError::ChannelClosed),
452 Either::Right(_) => Err(SubscriptionAckError::Timeout),
453 }
454 }
455
456 pub fn resolve_ack(&self, ack: &ProtoSubscriptionAck) {
458 tracing::debug!(ack = %ack.subscription_id, "subscription ack received");
459 let sender = {
460 let mut pending = self.pending_acks.lock();
461 pending.remove(&ack.subscription_id)
462 };
463
464 if let Some(sender) = sender {
465 let _ = sender.send(if ack.success {
466 Ok(())
467 } else {
468 Err(SubscriptionAckError::Rejected {
469 message: if ack.error.is_empty() {
470 "subscription ack failed".to_string()
471 } else {
472 ack.error.clone()
473 },
474 })
475 });
476 } else {
477 tracing::info!(
478 ack_id = %ack.subscription_id,
479 "received subscription ack with no pending waiter"
480 );
481 }
482 }
483}