1use async_trait::async_trait;
2use futures::Stream;
3use futures::ready;
4use std::collections::{HashMap, VecDeque};
5use std::fs;
6use std::path::PathBuf;
7use std::pin::Pin;
8use std::str::FromStr;
9use std::sync::atomic::{AtomicU64, Ordering};
10use std::task::{Context, Poll};
11use tonic::Request;
12use tonic::metadata::MetadataValue;
13use tonic::transport::{Certificate, Channel, ClientTlsConfig, Endpoint};
14
15use tokio::runtime::{Handle, Runtime};
16use umadb_dcb::{
17 DcbAppendCondition, DcbError, DcbEvent, DcbEventStoreAsync, DcbEventStoreSync, DcbQuery,
18 DcbReadResponseAsync, DcbReadResponseSync, DcbResult, DcbSequencedEvent, DcbSubscriptionAsync,
19 DcbSubscriptionSync, ShutdownStatus, StreamCancelHandle, TrackingInfo,
20};
21
22use futures::FutureExt;
23use futures::future::BoxFuture;
24use std::sync::{Arc, Mutex, Once, OnceLock};
25use std::time::Duration;
26use tokio::sync::watch;
27
28static CANCEL_SENDER: OnceLock<watch::Sender<()>> = OnceLock::new();
30
31fn cancel_receiver() -> watch::Receiver<()> {
33 let sender = CANCEL_SENDER.get_or_init(|| {
34 let (tx, _rx) = watch::channel::<()>(());
35 tx
36 });
37 sender.subscribe()
38}
39
40pub fn cancel_all_stream_responses() {
42 if let Some(sender) = CANCEL_SENDER.get() {
43 let _ = sender.send(()); }
45}
46
47static REGISTER_SIGINT: Once = Once::new();
48
49pub fn register_cancel_sigint_handler() {
50 REGISTER_SIGINT.call_once(|| {
51 let handle = Handle::current();
53
54 handle.spawn(async {
56 if tokio::signal::ctrl_c().await.is_ok() {
57 cancel_all_stream_responses();
58 }
59 });
60 });
61}
62
63pub trait ShutdownEvaluator {
64 fn cancel_channel(&self) -> &watch::Receiver<()>;
66 fn local_cancel_channel(&self) -> &watch::Receiver<()>;
67 fn is_ended(&self) -> bool;
68
69 fn evaluate_shutdown(&self) -> ShutdownStatus {
70 if self.cancel_channel().has_changed().unwrap_or(false)
72 || self.local_cancel_channel().has_changed().unwrap_or(false)
73 {
74 return ShutdownStatus::CancelledByUser;
75 }
76
77 if self.is_ended() {
79 return ShutdownStatus::StoppedGracefully;
80 }
81
82 ShutdownStatus::NotStopped
83 }
84}
85
86pub trait IteratorWithTimeout {
87 type Item;
88
89 fn next_timeout(&mut self, timeout: Duration) -> Option<Self::Item>;
91}
92
93pub struct AsyncClientReadResponse {
95 stream: tonic::Streaming<umadb_proto::v1::ReadResponse>,
96 buffer: VecDeque<DcbSequencedEvent>,
97 last_head: Option<Option<u64>>, ended: bool,
99 cancel: watch::Receiver<()>,
100 local_cancel_tx: Arc<watch::Sender<()>>,
102 local_cancel: watch::Receiver<()>,
103}
104
105impl AsyncClientReadResponse {
106 pub fn new(stream: tonic::Streaming<umadb_proto::v1::ReadResponse>) -> Self {
107 let (local_cancel_tx, local_cancel) = watch::channel(());
108 let local_cancel_tx = Arc::new(local_cancel_tx);
109 Self {
110 stream,
111 buffer: VecDeque::new(),
112 last_head: None,
113 ended: false,
114 cancel: cancel_receiver(),
115 local_cancel_tx,
116 local_cancel,
117 }
118 }
119
120 async fn fetch_next_if_needed(&mut self) -> DcbResult<()> {
122 if !self.buffer.is_empty() || self.ended {
123 return Ok(());
124 }
125
126 tokio::select! {
127 biased;
128 _ = self.cancel.changed() => {
129 self.ended = true;
130 return Err(DcbError::CancelledByUser());
131 }
132 _ = self.local_cancel.changed() => {
133 self.ended = true;
134 return Err(DcbError::CancelledByUser());
135 }
136 msg = self.stream.message() => {
137 match msg {
138 Ok(Some(resp)) => {
139 self.last_head = Some(resp.head);
140 let mut buffer = VecDeque::with_capacity(resp.events.len());
141 for e in resp.events {
142 if let Some(ev) = e.event {
143 let event = DcbEvent::try_from(ev)?;
144 buffer.push_back(DcbSequencedEvent { position: e.position, event });
145 }
146 }
147 self.buffer = buffer;
148 }
149 Ok(None) => self.ended = true,
150 Err(status) => return Err(umadb_proto::dcb_error_from_status(status)),
151 }
152 }
153 }
154
155 Ok(())
156 }
157
158 async fn fetch_next_if_needed_timeout(&mut self, timeout: Duration) -> DcbResult<()> {
159 if !self.buffer.is_empty() || self.ended {
160 return Ok(());
161 }
162
163 tokio::select! {
164 biased;
165 _ = self.cancel.changed() => {
166 self.ended = true;
167 return Err(DcbError::CancelledByUser());
168 }
169 _ = self.local_cancel.changed() => {
170 self.ended = true;
171 return Err(DcbError::CancelledByUser());
172 }
173 _ = tokio::time::sleep(timeout) => {
174 return Err(DcbError::Timeout());
175 }
176 msg = self.stream.message() => {
177 match msg {
178 Ok(Some(resp)) => {
179 self.last_head = Some(resp.head);
180 let mut buffer = VecDeque::with_capacity(resp.events.len());
181 for e in resp.events {
182 if let Some(ev) = e.event {
183 let event = DcbEvent::try_from(ev)?;
184 buffer.push_back(DcbSequencedEvent { position: e.position, event });
185 }
186 }
187 self.buffer = buffer;
188 }
189 Ok(None) => self.ended = true,
190 Err(status) => return Err(umadb_proto::dcb_error_from_status(status)),
191 }
192 }
193 }
194
195 Ok(())
196 }
197}
198
199impl ShutdownEvaluator for AsyncClientReadResponse {
200 fn cancel_channel(&self) -> &watch::Receiver<()> {
201 &self.cancel
202 }
203 fn local_cancel_channel(&self) -> &watch::Receiver<()> {
204 &self.local_cancel
205 }
206 fn is_ended(&self) -> bool {
207 self.ended
208 }
209}
210
211#[async_trait]
212impl DcbReadResponseAsync for AsyncClientReadResponse {
213 async fn head(&mut self) -> DcbResult<Option<u64>> {
214 if let Some(h) = self.last_head {
215 return Ok(h);
216 }
217 self.fetch_next_if_needed().await?;
219 Ok(self.last_head.unwrap_or(None))
220 }
221
222 async fn next_batch(&mut self) -> DcbResult<Vec<DcbSequencedEvent>> {
223 if !self.buffer.is_empty() {
224 return Ok(self.buffer.drain(..).collect());
225 }
226
227 self.fetch_next_if_needed().await?;
228
229 if !self.buffer.is_empty() {
230 return Ok(self.buffer.drain(..).collect());
231 }
232
233 Ok(Vec::new())
234 }
235
236 async fn next_batch_timeout(&mut self, timeout: Duration) -> DcbResult<Vec<DcbSequencedEvent>> {
237 if !self.buffer.is_empty() {
238 return Ok(self.buffer.drain(..).collect());
239 }
240
241 self.fetch_next_if_needed_timeout(timeout).await?;
243
244 if !self.buffer.is_empty() {
245 return Ok(self.buffer.drain(..).collect());
246 }
247
248 Ok(Vec::new())
249 }
250
251 fn cancel(&mut self) {
252 let _ = self.local_cancel_tx.send(());
253 }
254
255 fn cancel_handle(&self) -> Option<StreamCancelHandle> {
256 let tx = self.local_cancel_tx.clone();
257 Some(StreamCancelHandle::new(move || {
258 let _ = tx.send(());
259 }))
260 }
261
262 fn check_shutdown_status(&self) -> ShutdownStatus {
263 <Self as ShutdownEvaluator>::evaluate_shutdown(self)
264 }
265}
266
267impl Stream for AsyncClientReadResponse {
268 type Item = DcbResult<DcbSequencedEvent>;
269
270 fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
271 let this = self.get_mut();
272
273 loop {
274 match this.evaluate_shutdown() {
275 ShutdownStatus::CancelledByUser => {
276 this.buffer.clear();
277 return Poll::Ready(Some(Err(DcbError::CancelledByUser())));
279 }
280 ShutdownStatus::StoppedGracefully => {
281 this.buffer.clear();
282 return Poll::Ready(None);
284 }
285 ShutdownStatus::NotStopped => {}
286 }
287
288 if let Some(ev) = this.buffer.pop_front() {
290 return Poll::Ready(Some(Ok(ev)));
291 }
292
293 return match ready!(Pin::new(&mut this.stream).poll_next(cx)) {
295 Some(Ok(resp)) => {
296 this.last_head = Some(resp.head);
297
298 let mut buffer = VecDeque::with_capacity(resp.events.len());
299 for e in resp.events {
300 if let Some(ev) = e.event {
301 let event = match DcbEvent::try_from(ev) {
302 Ok(event) => event,
303 Err(err) => return Poll::Ready(Some(Err(err))),
304 };
305 buffer.push_back(DcbSequencedEvent {
306 position: e.position,
307 event,
308 });
309 }
310 }
311
312 this.buffer = buffer;
313
314 if this.buffer.is_empty() {
316 continue;
317 }
318
319 let ev = this.buffer.pop_front().unwrap();
321 Poll::Ready(Some(Ok(ev)))
322 }
323 Some(Err(status)) => {
324 this.ended = true;
325 Poll::Ready(Some(Err(umadb_proto::dcb_error_from_status(status))))
326 }
327 None => {
328 this.ended = true;
329 Poll::Ready(None)
330 }
331 };
332 }
333 }
334}
335
336pub struct AsyncClientSubscription {
338 stream: tonic::Streaming<umadb_proto::v1::SubscribeResponse>,
339 buffer: VecDeque<DcbSequencedEvent>,
340 ended: bool,
341 cancel: watch::Receiver<()>,
342 local_cancel_tx: Arc<watch::Sender<()>>,
344 local_cancel: watch::Receiver<()>,
345}
346
347impl AsyncClientSubscription {
348 pub fn new(stream: tonic::Streaming<umadb_proto::v1::SubscribeResponse>) -> Self {
349 let (local_stop_tx, local_cancel) = watch::channel(());
350 let local_cancel_tx = Arc::new(local_stop_tx);
351 Self {
352 stream,
353 buffer: VecDeque::new(),
354 ended: false,
355 cancel: cancel_receiver(),
356 local_cancel_tx,
357 local_cancel,
358 }
359 }
360
361 async fn fetch_next_if_needed(&mut self, timeout: Duration) -> DcbResult<()> {
362 if !self.buffer.is_empty() || self.ended {
363 return Ok(());
364 }
365
366 tokio::select! {
367 biased;
368 _ = self.cancel.changed() => {
369 self.ended = true;
370 return Err(DcbError::CancelledByUser());
371 }
372 _ = self.local_cancel.changed() => {
373 self.ended = true;
374 return Err(DcbError::CancelledByUser());
375 }
376 _ = tokio::time::sleep(timeout) => {
377 return Err(DcbError::Timeout());
378 }
379 msg = self.stream.message() => {
380 match msg {
381 Ok(Some(resp)) => {
382 let mut buffer = VecDeque::with_capacity(resp.events.len());
383 for e in resp.events {
384 if let Some(ev) = e.event {
385 let event = DcbEvent::try_from(ev)?;
386 buffer.push_back(DcbSequencedEvent { position: e.position, event });
387 }
388 }
389 self.buffer = buffer;
390 }
391 Ok(None) => self.ended = true,
392 Err(status) => return Err(umadb_proto::dcb_error_from_status(status)),
393 }
394 }
395 }
396 Ok(())
397 }
398}
399
400impl ShutdownEvaluator for AsyncClientSubscription {
401 fn cancel_channel(&self) -> &watch::Receiver<()> {
402 &self.cancel
403 }
404 fn local_cancel_channel(&self) -> &watch::Receiver<()> {
405 &self.local_cancel
406 }
407 fn is_ended(&self) -> bool {
408 self.ended
409 }
410}
411
412#[async_trait]
413impl DcbSubscriptionAsync for AsyncClientSubscription {
414 async fn next_batch(&mut self) -> DcbResult<Vec<DcbSequencedEvent>> {
415 if !self.buffer.is_empty() {
416 return Ok(self.buffer.drain(..).collect());
417 }
418 self.fetch_next_if_needed(Duration::from_secs(u64::MAX))
419 .await?;
420 if !self.buffer.is_empty() {
421 return Ok(self.buffer.drain(..).collect());
422 }
423 Ok(Vec::new())
424 }
425
426 async fn next_batch_timeout(&mut self, timeout: Duration) -> DcbResult<Vec<DcbSequencedEvent>> {
427 if !self.buffer.is_empty() {
428 return Ok(self.buffer.drain(..).collect());
429 }
430 self.fetch_next_if_needed(timeout).await?;
431 if !self.buffer.is_empty() {
432 return Ok(self.buffer.drain(..).collect());
433 }
434 Ok(Vec::new())
435 }
436
437 fn cancel(&mut self) {
438 let _ = self.local_cancel_tx.send(());
439 }
440
441 fn cancel_handle(&self) -> Option<StreamCancelHandle> {
442 let tx = self.local_cancel_tx.clone();
443 Some(StreamCancelHandle::new(move || {
444 let _ = tx.send(());
445 }))
446 }
447
448 fn check_shutdown_status(&self) -> ShutdownStatus {
449 <Self as ShutdownEvaluator>::evaluate_shutdown(self)
450 }
451}
452
453impl Stream for AsyncClientSubscription {
454 type Item = DcbResult<DcbSequencedEvent>;
455
456 fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
457 let this = self.get_mut();
458
459 loop {
460 match this.evaluate_shutdown() {
461 ShutdownStatus::CancelledByUser => {
462 this.buffer.clear();
463 return Poll::Ready(Some(Err(DcbError::CancelledByUser())));
465 }
466 ShutdownStatus::StoppedGracefully => {
467 this.buffer.clear();
468 return Poll::Ready(None);
470 }
471 ShutdownStatus::NotStopped => {}
472 }
473
474 if let Some(ev) = this.buffer.pop_front() {
475 return Poll::Ready(Some(Ok(ev)));
476 }
477
478 return match ready!(Pin::new(&mut this.stream).poll_next(cx)) {
479 Some(Ok(resp)) => {
480 let mut buffer = VecDeque::with_capacity(resp.events.len());
481 for e in resp.events {
482 if let Some(ev) = e.event {
483 let event = match DcbEvent::try_from(ev) {
484 Ok(event) => event,
485 Err(err) => return Poll::Ready(Some(Err(err))),
486 };
487 buffer.push_back(DcbSequencedEvent {
488 position: e.position,
489 event,
490 });
491 }
492 }
493 this.buffer = buffer;
494 if this.buffer.is_empty() {
495 continue;
496 }
497 let ev = this.buffer.pop_front().unwrap();
498 Poll::Ready(Some(Ok(ev)))
499 }
500 Some(Err(status)) => {
501 this.ended = true;
502 Poll::Ready(Some(Err(umadb_proto::dcb_error_from_status(status))))
503 }
504 None => {
505 this.ended = true;
506 Poll::Ready(None)
507 }
508 };
509 }
510 }
511}
512
513pub trait AsyncStreamBackend: Send + Unpin {
515 fn next_batch_box(&mut self) -> BoxFuture<'_, DcbResult<Vec<DcbSequencedEvent>>>;
516 fn next_batch_timeout_box(
517 &mut self,
518 timeout: Duration,
519 ) -> BoxFuture<'_, DcbResult<Vec<DcbSequencedEvent>>>;
520 fn cancel(&mut self);
521 fn check_shutdown_status(&self) -> ShutdownStatus;
522}
523
524impl AsyncStreamBackend for dyn DcbReadResponseAsync + Send + 'static {
526 fn next_batch_box(&mut self) -> BoxFuture<'_, DcbResult<Vec<DcbSequencedEvent>>> {
527 self.next_batch().boxed()
528 }
529 fn next_batch_timeout_box(
530 &mut self,
531 timeout: Duration,
532 ) -> BoxFuture<'_, DcbResult<Vec<DcbSequencedEvent>>> {
533 self.next_batch_timeout(timeout).boxed()
534 }
535 fn cancel(&mut self) {
536 self.cancel();
537 }
538
539 fn check_shutdown_status(&self) -> ShutdownStatus {
540 self.check_shutdown_status()
541 }
542}
543
544impl AsyncStreamBackend for dyn DcbSubscriptionAsync + Send + 'static {
546 fn next_batch_box(&mut self) -> BoxFuture<'_, DcbResult<Vec<DcbSequencedEvent>>> {
547 self.next_batch().boxed()
548 }
549 fn next_batch_timeout_box(
550 &mut self,
551 timeout: Duration,
552 ) -> BoxFuture<'_, DcbResult<Vec<DcbSequencedEvent>>> {
553 self.next_batch_timeout(timeout).boxed()
554 }
555 fn cancel(&mut self) {
556 self.cancel();
557 }
558 fn check_shutdown_status(&self) -> ShutdownStatus {
559 self.check_shutdown_status()
560 }
561}
562
563pub struct SyncStreamEngine<A: AsyncStreamBackend + ?Sized> {
564 pub rt: Handle,
565 pub finished: bool,
566 pub buffer: VecDeque<DcbSequencedEvent>,
567 pub async_resp: Box<A>,
568}
569
570impl<A: AsyncStreamBackend + ?Sized> SyncStreamEngine<A> {
571 pub fn new(rt: Handle, async_resp: Box<A>) -> Self {
572 Self {
573 rt,
574 finished: false,
575 buffer: VecDeque::new(),
576 async_resp,
577 }
578 }
579
580 pub fn next_item(&mut self) -> Option<DcbResult<DcbSequencedEvent>> {
582 if !self.finished {
584 match self.async_resp.check_shutdown_status() {
585 ShutdownStatus::CancelledByUser => {
586 self.finished = true;
587 self.buffer.clear();
588 return Some(Err(DcbError::CancelledByUser()));
589 }
590 ShutdownStatus::StoppedGracefully => {
591 self.finished = true;
592 self.buffer.clear();
593 return None; }
595 ShutdownStatus::NotStopped => {}
596 }
597 }
598
599 while self.buffer.is_empty() && !self.finished {
600 let res = self.rt.block_on(self.async_resp.next_batch_box());
601 match res {
602 Ok(batch) => {
603 if batch.is_empty() {
604 self.finished = true;
605 } else {
606 self.buffer = batch.into();
607 }
608 }
609 Err(e) => return Some(Err(e)),
610 }
611 }
612
613 self.buffer.pop_front().map(Ok)
614 }
615
616 pub fn next_item_timeout(&mut self, timeout: Duration) -> Option<DcbResult<DcbSequencedEvent>> {
618 if !self.finished {
619 match self.async_resp.check_shutdown_status() {
620 ShutdownStatus::CancelledByUser => {
621 self.finished = true;
622 self.buffer.clear();
623 return Some(Err(DcbError::CancelledByUser()));
624 }
625 ShutdownStatus::StoppedGracefully => {
626 self.finished = true;
627 self.buffer.clear();
628 return None; }
630 ShutdownStatus::NotStopped => {}
631 }
632 }
633
634 if let Some(ev) = self.buffer.pop_front() {
635 return Some(Ok(ev));
636 }
637
638 if self.finished {
639 return None;
640 }
641
642 let res = self
643 .rt
644 .block_on(self.async_resp.next_batch_timeout_box(timeout));
645 match res {
646 Ok(batch) => {
647 if batch.is_empty() {
648 self.finished = true;
649 } else {
650 self.buffer = batch.into();
651 }
652 }
653 Err(DcbError::Timeout()) => return Some(Err(DcbError::Timeout())),
654 Err(e) => return Some(Err(e)),
655 }
656
657 self.buffer.pop_front().map(Ok)
658 }
659
660 pub fn engine_next_batch(&mut self) -> DcbResult<Vec<DcbSequencedEvent>> {
662 if !self.buffer.is_empty() {
663 return Ok(self.buffer.drain(..).collect());
664 }
665
666 let batch = self.rt.block_on(self.async_resp.next_batch_box())?;
668 if batch.is_empty() {
669 self.finished = true;
670 } else {
671 self.buffer = batch.into();
672 }
673 Ok(self.buffer.drain(..).collect())
674 }
675
676 pub fn engine_next_batch_timeout(
678 &mut self,
679 timeout: Duration,
680 ) -> DcbResult<Vec<DcbSequencedEvent>> {
681 if !self.buffer.is_empty() {
682 return Ok(self.buffer.drain(..).collect());
683 }
684
685 let batch = self
686 .rt
687 .block_on(self.async_resp.next_batch_timeout_box(timeout))?;
688 if batch.is_empty() {
689 self.finished = true;
690 } else {
691 self.buffer = batch.into();
692 }
693 Ok(self.buffer.drain(..).collect())
694 }
695
696 pub fn engine_stop(&mut self) {
698 self.finished = true;
699 self.async_resp.cancel();
700 }
701}
702
703pub struct SyncClientReadResponse {
704 engine: SyncStreamEngine<dyn DcbReadResponseAsync + Send + 'static>,
705}
706
707impl SyncClientReadResponse {
708 pub fn new(rt: Handle, async_resp: Box<dyn DcbReadResponseAsync + Send + 'static>) -> Self {
709 Self {
710 engine: SyncStreamEngine::new(rt, async_resp),
711 }
712 }
713}
714
715impl Iterator for SyncClientReadResponse {
716 type Item = DcbResult<DcbSequencedEvent>;
717
718 fn next(&mut self) -> Option<Self::Item> {
719 self.engine.next_item()
720 }
721}
722
723impl IteratorWithTimeout for SyncClientReadResponse {
724 type Item = DcbResult<DcbSequencedEvent>;
725
726 fn next_timeout(&mut self, timeout: Duration) -> Option<Self::Item> {
727 self.engine.next_item_timeout(timeout)
729 }
730}
731
732impl DcbReadResponseSync for SyncClientReadResponse {
733 fn head(&mut self) -> DcbResult<Option<u64>> {
734 self.engine.rt.block_on(self.engine.async_resp.head())
735 }
736
737 fn collect_with_head(&mut self) -> DcbResult<(Vec<DcbSequencedEvent>, Option<u64>)> {
738 let mut out = Vec::new();
739 for result in self.by_ref() {
740 out.push(result?);
741 }
742 Ok((out, self.head()?))
743 }
744
745 fn next_batch(&mut self) -> DcbResult<Vec<DcbSequencedEvent>> {
746 self.engine.engine_next_batch()
747 }
748
749 fn cancel(&mut self) {
750 self.engine.engine_stop();
751 }
752
753 fn next_timeout(&mut self, timeout: Duration) -> Option<DcbResult<DcbSequencedEvent>> {
754 <Self as IteratorWithTimeout>::next_timeout(self, timeout)
755 }
756}
757
758pub struct SyncClientSubscription {
759 engine: SyncStreamEngine<dyn DcbSubscriptionAsync + Send + 'static>,
760}
761
762impl SyncClientSubscription {
763 pub fn new(rt: Handle, async_resp: Box<dyn DcbSubscriptionAsync + Send + 'static>) -> Self {
764 Self {
765 engine: SyncStreamEngine::new(rt, async_resp),
766 }
767 }
768}
769
770impl Iterator for SyncClientSubscription {
771 type Item = DcbResult<DcbSequencedEvent>;
772
773 fn next(&mut self) -> Option<Self::Item> {
774 self.engine.next_item()
775 }
776}
777
778impl IteratorWithTimeout for SyncClientSubscription {
779 type Item = DcbResult<DcbSequencedEvent>;
780
781 fn next_timeout(&mut self, timeout: Duration) -> Option<Self::Item> {
782 self.engine.next_item_timeout(timeout)
783 }
784}
785
786impl DcbSubscriptionSync for SyncClientSubscription {
787 fn next_batch(&mut self) -> DcbResult<Vec<DcbSequencedEvent>> {
788 self.engine.engine_next_batch()
789 }
790
791 fn next_batch_timeout(&mut self, timeout: Duration) -> DcbResult<Vec<DcbSequencedEvent>> {
792 self.engine.engine_next_batch_timeout(timeout)
793 }
794
795 fn cancel(&mut self) {
796 self.engine.async_resp.cancel();
797 }
798
799 fn next_timeout(&mut self, timeout: Duration) -> Option<DcbResult<DcbSequencedEvent>> {
800 <Self as IteratorWithTimeout>::next_timeout(self, timeout)
801 }
802}
803
804#[derive(Clone, Default)]
810struct StreamRegistry {
811 next_id: Arc<AtomicU64>,
812 active: Arc<Mutex<HashMap<u64, StreamCancelHandle>>>,
813}
814
815impl StreamRegistry {
816 fn register(&self, handle: Option<StreamCancelHandle>) -> StreamGuard {
819 let id = self.next_id.fetch_add(1, Ordering::Relaxed);
820 if let Some(handle) = handle {
821 self.active.lock().unwrap().insert(id, handle);
822 }
823 StreamGuard {
824 active: self.active.clone(),
825 id,
826 }
827 }
828
829 fn cancel_all(&self) {
831 let mut lock = self.active.lock().unwrap();
832 for (_id, handle) in lock.drain() {
833 handle.cancel();
834 }
835 }
836}
837
838struct StreamGuard {
840 active: Arc<Mutex<HashMap<u64, StreamCancelHandle>>>,
841 id: u64,
842}
843
844impl Drop for StreamGuard {
845 fn drop(&mut self) {
846 if let Ok(mut lock) = self.active.lock() {
847 lock.remove(&self.id);
848 }
849 }
850}
851
852struct RegisteredReadResponse {
855 inner: Box<dyn DcbReadResponseAsync + Send + 'static>,
856 _guard: StreamGuard,
857}
858
859impl Stream for RegisteredReadResponse {
860 type Item = DcbResult<DcbSequencedEvent>;
861
862 fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
863 Pin::new(&mut self.get_mut().inner).poll_next(cx)
865 }
866}
867
868#[async_trait]
869impl DcbReadResponseAsync for RegisteredReadResponse {
870 async fn head(&mut self) -> DcbResult<Option<u64>> {
871 self.inner.head().await
872 }
873
874 async fn next_batch(&mut self) -> DcbResult<Vec<DcbSequencedEvent>> {
875 self.inner.next_batch().await
876 }
877
878 async fn next_batch_timeout(&mut self, timeout: Duration) -> DcbResult<Vec<DcbSequencedEvent>> {
879 self.inner.next_batch_timeout(timeout).await
880 }
881
882 fn cancel(&mut self) {
883 self.inner.cancel();
884 }
885
886 fn cancel_handle(&self) -> Option<StreamCancelHandle> {
887 self.inner.cancel_handle()
888 }
889
890 fn check_shutdown_status(&self) -> ShutdownStatus {
891 self.inner.check_shutdown_status()
892 }
893}
894
895struct RegisteredSubscription {
898 inner: Box<dyn DcbSubscriptionAsync + Send + 'static>,
899 _guard: StreamGuard,
900}
901
902impl Stream for RegisteredSubscription {
903 type Item = DcbResult<DcbSequencedEvent>;
904
905 fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
906 Pin::new(&mut self.get_mut().inner).poll_next(cx)
907 }
908}
909
910#[async_trait]
911impl DcbSubscriptionAsync for RegisteredSubscription {
912 async fn next_batch(&mut self) -> DcbResult<Vec<DcbSequencedEvent>> {
913 self.inner.next_batch().await
914 }
915
916 async fn next_batch_timeout(&mut self, timeout: Duration) -> DcbResult<Vec<DcbSequencedEvent>> {
917 self.inner.next_batch_timeout(timeout).await
918 }
919
920 fn cancel(&mut self) {
921 self.inner.cancel();
922 }
923
924 fn cancel_handle(&self) -> Option<StreamCancelHandle> {
925 self.inner.cancel_handle()
926 }
927
928 fn check_shutdown_status(&self) -> ShutdownStatus {
929 self.inner.check_shutdown_status()
930 }
931}
932
933#[derive(Clone, Debug, Default)]
934pub struct ClientTlsOptions {
935 pub domain: Option<String>,
936 pub ca_pem: Option<Vec<u8>>, }
938
939fn new_endpoint(
940 url: String,
941 tls: Option<ClientTlsOptions>,
942) -> Result<Endpoint, tonic::transport::Error> {
943 let mut url_owned = url.to_string();
945 if url_owned.starts_with("grpcs://") {
946 url_owned = url_owned.replacen("grpcs://", "https://", 1);
947 }
948
949 let mut endpoint = Endpoint::from_shared(url_owned)?
950 .tcp_nodelay(true)
951 .http2_keep_alive_interval(Duration::from_secs(5))
952 .keep_alive_timeout(Duration::from_secs(10))
953 .initial_stream_window_size(Some(4 * 1024 * 1024))
954 .initial_connection_window_size(Some(8 * 1024 * 1024));
955
956 if let Some(opts) = tls {
957 let mut cfg = ClientTlsConfig::new();
958 if let Some(domain) = &opts.domain {
959 cfg = cfg.domain_name(domain.clone());
960 }
961 if let Some(ca) = opts.ca_pem {
962 cfg = cfg.ca_certificate(Certificate::from_pem(ca));
963 }
964 endpoint = endpoint.tls_config(cfg)?;
965 } else if url.starts_with("https://") {
966 endpoint = endpoint.tls_config(ClientTlsConfig::new())?;
968 }
969
970 Ok(endpoint)
971}
972
973async fn new_channel(
974 url: String,
975 tls: Option<ClientTlsOptions>,
976) -> Result<Channel, tonic::transport::Error> {
977 new_endpoint(url, tls)?.connect().await
978}
979
980pub struct AsyncUmaDbClient {
982 client: umadb_proto::v1::dcb_client::DcbClient<Channel>,
983 batch_size: Option<u32>,
984 tls_enabled: bool,
985 api_key: Option<String>,
986 registry: StreamRegistry,
988}
989
990impl AsyncUmaDbClient {
991 pub async fn connect(
992 url: String,
993 ca_path: Option<String>,
994 batch_size: Option<u32>,
995 api_key: Option<String>,
996 ) -> DcbResult<Self> {
997 let ca_pem = {
999 if let Some(ca_path) = ca_path {
1000 let ca_path = PathBuf::from(ca_path);
1001 Some(
1002 fs::read(&ca_path)
1003 .unwrap_or_else(|_| panic!("couldn't read cert_path: {:?}", ca_path)),
1004 )
1005 } else {
1006 None
1007 }
1008 };
1009
1010 let client_tls_options = Some(ClientTlsOptions {
1011 domain: None,
1012 ca_pem,
1013 });
1014
1015 Self::connect_with_tls_options(url, client_tls_options, batch_size, api_key).await
1016 }
1017
1018 pub async fn subscribe(
1019 &self,
1020 query: Option<DcbQuery>,
1021 after: Option<u64>,
1022 ) -> DcbResult<Box<dyn DcbSubscriptionAsync + Send + 'static>> {
1023 let query_proto = query.map(|q| q.into());
1024 let mut client = self.client.clone();
1025 let req_body = umadb_proto::v1::SubscribeRequest {
1026 query: query_proto,
1027 after,
1028 batch_size: self.batch_size,
1029 };
1030 let req = self.add_auth(Request::new(req_body))?;
1031 let response = client
1032 .subscribe(req)
1033 .await
1034 .map_err(umadb_proto::dcb_error_from_status)?;
1035 let stream = response.into_inner();
1036 let inner: Box<dyn DcbSubscriptionAsync + Send + 'static> =
1037 Box::new(AsyncClientSubscription::new(stream));
1038 let guard = self.registry.register(inner.cancel_handle());
1039 Ok(Box::new(RegisteredSubscription {
1040 inner,
1041 _guard: guard,
1042 }))
1043 }
1044
1045 pub fn close(&self) {
1051 self.registry.cancel_all();
1052 }
1053
1054 pub async fn connect_with_tls_options(
1055 url: String,
1056 tls_options: Option<ClientTlsOptions>,
1057 batch_size: Option<u32>,
1058 api_key: Option<String>,
1059 ) -> DcbResult<Self> {
1060 let tls_enabled = url.starts_with("https://") || url.starts_with("grpcs://");
1061 match new_channel(url, tls_options).await {
1062 Ok(channel) => Ok(Self {
1063 client: umadb_proto::v1::dcb_client::DcbClient::new(channel),
1064 batch_size,
1065 tls_enabled,
1066 api_key,
1067 registry: StreamRegistry::default(),
1068 }),
1069 Err(err) => Err(DcbError::TransportError(format!("{err}"))),
1070 }
1071 }
1072
1073 fn add_auth<T>(&self, mut req: Request<T>) -> DcbResult<Request<T>> {
1074 if let Some(key) = &self.api_key {
1075 if !self.tls_enabled {
1076 return Err(DcbError::TransportError(
1077 "API key configured but TLS is not enabled; refusing to send credentials over insecure channel".to_string(),
1078 ));
1079 }
1080 let token = MetadataValue::from_str(&format!("Bearer {}", key))
1081 .map_err(|e| DcbError::TransportError(format!("invalid API key: {}", e)))?;
1082 req.metadata_mut().insert("authorization", token);
1083 }
1084 Ok(req)
1085 }
1086}
1087
1088#[async_trait]
1089impl DcbEventStoreAsync for AsyncUmaDbClient {
1090 async fn read<'a>(
1092 &'a self,
1093 query: Option<DcbQuery>,
1094 start: Option<u64>,
1095 backwards: bool,
1096 limit: Option<u32>,
1097 ) -> DcbResult<Box<dyn DcbReadResponseAsync + Send + 'static>> {
1098 let query_proto = query.map(|q| q.into());
1099 #[allow(deprecated)]
1100 let req_body = umadb_proto::v1::ReadRequest {
1101 query: query_proto,
1102 start,
1103 backwards: Some(backwards),
1104 limit,
1105 subscribe: None,
1106 batch_size: self.batch_size,
1107 };
1108 let mut client = self.client.clone();
1109 let req = self.add_auth(Request::new(req_body))?;
1110 let response = client
1111 .read(req)
1112 .await
1113 .map_err(umadb_proto::dcb_error_from_status)?;
1114 let stream = response.into_inner();
1115 let inner: Box<dyn DcbReadResponseAsync + Send + 'static> =
1116 Box::new(AsyncClientReadResponse::new(stream));
1117 let guard = self.registry.register(inner.cancel_handle());
1118 Ok(Box::new(RegisteredReadResponse {
1119 inner,
1120 _guard: guard,
1121 }))
1122 }
1123
1124 async fn head(&self) -> DcbResult<Option<u64>> {
1125 let mut client = self.client.clone();
1126 let req = self.add_auth(Request::new(umadb_proto::v1::HeadRequest {}))?;
1127 match client.head(req).await {
1128 Ok(response) => Ok(response.into_inner().position),
1129 Err(status) => Err(umadb_proto::dcb_error_from_status(status)),
1130 }
1131 }
1132
1133 async fn get_tracking_info(&self, source: &str) -> DcbResult<Option<u64>> {
1134 let mut client = self.client.clone();
1135 let req = self.add_auth(Request::new(umadb_proto::v1::TrackingRequest {
1136 source: source.to_string(),
1137 }))?;
1138 match client.get_tracking_info(req).await {
1139 Ok(response) => Ok(response.into_inner().position),
1140 Err(status) => Err(umadb_proto::dcb_error_from_status(status)),
1141 }
1142 }
1143
1144 async fn append(
1145 &self,
1146 events: Vec<DcbEvent>,
1147 condition: Option<DcbAppendCondition>,
1148 tracking_info: Option<TrackingInfo>,
1149 ) -> DcbResult<u64> {
1150 let events_proto: Vec<umadb_proto::v1::Event> = events
1151 .into_iter()
1152 .map(umadb_proto::v1::Event::from)
1153 .collect();
1154 let condition_proto = condition.map(|c| umadb_proto::v1::AppendCondition {
1155 fail_if_events_match: Some(c.fail_if_events_match.into()),
1156 after: c.after,
1157 });
1158 let tracking_info_proto = tracking_info.map(|t| umadb_proto::v1::TrackingInfo {
1159 source: t.source,
1160 position: t.position,
1161 });
1162 let body = umadb_proto::v1::AppendRequest {
1163 events: events_proto,
1164 condition: condition_proto,
1165 tracking_info: tracking_info_proto,
1166 };
1167 let mut client = self.client.clone();
1168 let req = self.add_auth(Request::new(body))?;
1169 match client.append(req).await {
1170 Ok(response) => Ok(response.into_inner().position),
1171 Err(status) => Err(umadb_proto::dcb_error_from_status(status)),
1172 }
1173 }
1174}
1175
1176impl Drop for AsyncUmaDbClient {
1177 fn drop(&mut self) {
1178 self.registry.cancel_all();
1181 }
1182}
1183
1184pub struct SyncUmaDbClient {
1186 async_client: AsyncUmaDbClient,
1187 handle: Handle,
1188 _runtime: Option<Runtime>, }
1190
1191impl SyncUmaDbClient {
1192 pub fn connect(
1193 url: String,
1194 ca_path: Option<String>,
1195 batch_size: Option<u32>,
1196 api_key: Option<String>,
1197 ) -> DcbResult<Self> {
1198 let (rt, handle) = Self::get_rt_handle();
1199 let async_client =
1200 handle.block_on(AsyncUmaDbClient::connect(url, ca_path, batch_size, api_key))?;
1201 Ok(Self {
1202 async_client,
1203 _runtime: rt, handle,
1205 })
1206 }
1207
1208 pub fn subscribe(
1212 &self,
1213 query: Option<DcbQuery>,
1214 after: Option<u64>,
1215 ) -> DcbResult<Box<dyn DcbSubscriptionSync + Send + 'static>> {
1216 let async_subscription = self
1217 .handle
1218 .block_on(self.async_client.subscribe(query, after))?;
1219
1220 Ok(Box::new(SyncClientSubscription::new(
1222 self.handle.clone(),
1223 async_subscription,
1224 )))
1225 }
1226
1227 pub fn close(&self) {
1232 self.async_client.close();
1233 }
1234
1235 fn get_rt_handle() -> (Option<Runtime>, Handle) {
1255 let (rt, handle) = {
1256 if let Ok(handle) = Handle::try_current() {
1258 (None, handle)
1259 } else {
1260 let rt = Runtime::new().expect("failed to create Tokio runtime");
1262 let handle = rt.handle().clone();
1263 (Some(rt), handle)
1264 }
1265 };
1266 (rt, handle)
1267 }
1268}
1269
1270impl DcbEventStoreSync for SyncUmaDbClient {
1271 fn read(
1272 &self,
1273 query: Option<DcbQuery>,
1274 start: Option<u64>,
1275 backwards: bool,
1276 limit: Option<u32>,
1277 ) -> DcbResult<Box<dyn DcbReadResponseSync + Send + 'static>> {
1278 let async_read_response = self
1279 .handle
1280 .block_on(self.async_client.read(query, start, backwards, limit))?;
1281
1282 Ok(Box::new(SyncClientReadResponse::new(
1284 self.handle.clone(),
1285 async_read_response,
1286 )))
1287 }
1288
1289 fn head(&self) -> DcbResult<Option<u64>> {
1290 self.handle.block_on(self.async_client.head())
1291 }
1292
1293 fn get_tracking_info(&self, source: &str) -> DcbResult<Option<u64>> {
1294 self.handle
1295 .block_on(self.async_client.get_tracking_info(source))
1296 }
1297
1298 fn append(
1299 &self,
1300 events: Vec<DcbEvent>,
1301 condition: Option<DcbAppendCondition>,
1302 tracking_info: Option<TrackingInfo>,
1303 ) -> DcbResult<u64> {
1304 self.handle
1305 .block_on(self.async_client.append(events, condition, tracking_info))
1306 }
1307}
1308
1309pub struct UmaDbClient {
1310 url: String,
1311 ca_path: Option<String>,
1312 batch_size: Option<u32>,
1313 api_key: Option<String>,
1314}
1315
1316impl UmaDbClient {
1317 pub fn new(url: String) -> Self {
1318 Self {
1319 url,
1320 ca_path: None,
1321 batch_size: None,
1322 api_key: None,
1323 }
1324 }
1325
1326 pub fn ca_path(self, ca_path: String) -> Self {
1327 Self {
1328 ca_path: Some(ca_path),
1329 ..self
1330 }
1331 }
1332
1333 pub fn api_key(self, api_key: String) -> Self {
1334 Self {
1335 api_key: Some(api_key),
1336 ..self
1337 }
1338 }
1339
1340 pub fn batch_size(self, batch_size: u32) -> Self {
1341 Self {
1342 batch_size: Some(batch_size),
1343 ..self
1344 }
1345 }
1346
1347 pub fn connect(&self) -> DcbResult<SyncUmaDbClient> {
1348 let client = SyncUmaDbClient::connect(
1349 self.url.clone(),
1350 self.ca_path.clone(),
1351 self.batch_size,
1352 self.api_key.clone(),
1353 );
1354 client
1355 }
1356 pub async fn connect_async(&self) -> DcbResult<AsyncUmaDbClient> {
1357 let client = AsyncUmaDbClient::connect(
1358 self.url.clone(),
1359 self.ca_path.clone(),
1360 self.batch_size,
1361 self.api_key.clone(),
1362 )
1363 .await;
1364 client
1365 }
1366}