1use futures::Stream;
2use std::fs;
3use std::path::Path;
4use std::pin::Pin;
5use std::sync::Arc;
6use std::sync::LazyLock;
7use std::thread;
8use std::time::Instant;
9use tokio::sync::{Semaphore, mpsc, oneshot, watch};
10
11use tokio_stream::wrappers::ReceiverStream;
12use tonic::transport::{Identity, ServerTlsConfig};
13use tonic::{Request, Response, Status, transport::Server};
14use umadb_core::db::{
15 UmaDb, clone_dcb_error, is_integrity_error, is_invalid_argument_error, read_conditional,
16 shadow_for_batch_abort,
17};
18pub use umadb_core::mvcc::{
19 DEFAULT_DB_FILENAME, DEFAULT_PAGE_SIZE, Mvcc, ReadMethod, StorageOptions,
20};
21use umadb_dcb::{
22 DcbAppendCondition, DcbError, DcbEvent, DcbQuery, DcbResult, DcbSequencedEvent, TrackingInfo,
23};
24
25use tokio::runtime::Runtime;
26use tonic::codegen::http;
27use tonic::transport::server::TcpIncoming;
28use umadb_core::common::Position;
29
30use std::convert::Infallible;
31use std::future::Future;
32use std::task::{Context, Poll};
33use tonic::server::NamedService;
34use umadb_proto::status_from_dcb_error;
35
36#[derive(Clone, Debug)]
38pub struct ServerOptions {
39 pub listen_addr: String,
40 pub tls: Option<ServerTlsOptions>,
41 pub api_key: Option<String>,
42 pub storage: StorageOptions,
43}
44
45#[derive(Clone, Debug)]
47pub struct ServerTlsOptions {
48 pub cert_pem: Vec<u8>,
49 pub key_pem: Vec<u8>,
50}
51
52impl ServerTlsOptions {
53 pub fn from_path_strings(
54 cert_path: Option<String>,
55 key_path: Option<String>,
56 ) -> Result<Option<Self>, Box<dyn std::error::Error>> {
57 match (cert_path, key_path) {
58 (Some(cert_path), Some(key_path)) => {
59 let cert_pem = read_file(cert_path.clone(), "TLS certificate")?;
60 let key_pem = read_file(key_path.clone(), "TLS key")?;
61 Ok(Some(ServerTlsOptions { cert_pem, key_pem }))
62 }
63 (None, None) => Ok(None),
64 _ => Err("both cert_path and key_path must be provided for TLS".into()).into(),
65 }
66 }
67}
68
69fn read_file(path: String, purpose: &str) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
70 Ok(
71 fs::read(path.clone()).map_err(|e| -> Box<dyn std::error::Error> {
72 format!("failed to open {purpose} file '{path}': {}", e).into()
73 })?,
74 )
75}
76
77struct CancellationGuard(Option<oneshot::Sender<()>>);
79
80impl Drop for CancellationGuard {
81 fn drop(&mut self) {
82 if let Some(tx) = self.0.take() {
83 let _ = tx.send(());
84 }
85 }
86}
87
88#[derive(Clone, Debug)]
90pub struct PathRewriterService<S> {
91 inner: S,
92}
93
94impl<S> tower::Service<http::Request<tonic::body::Body>> for PathRewriterService<S>
95where
96 S: tower::Service<
97 http::Request<tonic::body::Body>,
98 Response = http::Response<tonic::body::Body>,
99 Error = Infallible,
100 > + Clone
101 + Send
102 + 'static,
103 S::Future: Send + 'static,
104{
105 type Response = S::Response;
106 type Error = S::Error;
107 type Future =
108 Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>> + Send + 'static>>;
109
110 fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
111 self.inner.poll_ready(cx)
112 }
113
114 fn call(&mut self, mut req: http::Request<tonic::body::Body>) -> Self::Future {
115 let uri = req.uri().clone();
116 let path = uri.path();
117
118 if path.starts_with("/umadb.UmaDBService/") {
120 let new_path_str = path.replace("/umadb.UmaDBService/", "/umadb.v1.DCB/");
121
122 let new_uri = if let (Some(scheme), Some(authority)) = (uri.scheme(), uri.authority()) {
125 http::Uri::builder()
127 .scheme(scheme.clone())
128 .authority(authority.clone())
129 .path_and_query(new_path_str.as_str())
130 .build()
131 .ok() } else {
133 new_path_str.parse::<http::Uri>().ok()
136 };
137
138 if let Some(final_uri) = new_uri {
139 *req.uri_mut() = final_uri;
140 } else {
141 eprintln!("failed to construct valid URI for path: {}", path);
142 }
143 }
144
145 let fut = self.inner.call(req);
146 Box::pin(fut)
147 }
148}
149
150impl<S: NamedService> NamedService for PathRewriterService<S> {
152 const NAME: &'static str = S::NAME;
153}
154
155#[derive(Clone, Debug)]
156pub struct PathRewriterLayer;
157
158impl<S> tower::Layer<S> for PathRewriterLayer
159where
160 S: tower::Service<
161 http::Request<tonic::body::Body>,
162 Response = http::Response<tonic::body::Body>,
163 Error = Infallible,
164 > + Clone
165 + Send
166 + 'static,
167 S::Future: Send + 'static,
168{
169 type Service = PathRewriterService<S>;
170
171 fn layer(&self, inner: S) -> Self::Service {
172 PathRewriterService { inner }
173 }
174}
175
176static START_TIME: LazyLock<Instant> = LazyLock::new(Instant::now);
177
178const APPEND_BATCH_MAX_EVENTS: usize = 2000;
179const READ_RESPONSE_BATCH_SIZE_DEFAULT: u32 = 100;
180const READ_RESPONSE_BATCH_SIZE_MAX: u32 = 5000;
181
182pub fn uptime() -> std::time::Duration {
183 START_TIME.elapsed()
184}
185
186fn build_server_builder_with_options(tls: Option<ServerTlsOptions>) -> Server {
187 use std::time::Duration;
188 let mut server_builder = Server::builder()
189 .http2_keepalive_interval(Some(Duration::from_secs(5)))
190 .http2_keepalive_timeout(Some(Duration::from_secs(10)))
191 .initial_stream_window_size(Some(4 * 1024 * 1024))
192 .initial_connection_window_size(Some(8 * 1024 * 1024))
193 .tcp_nodelay(true)
194 .concurrency_limit_per_connection(1024);
195
196 if let Some(opts) = tls {
197 let identity = Identity::from_pem(opts.cert_pem, opts.key_pem);
198 server_builder = server_builder
199 .tls_config(ServerTlsConfig::new().identity(identity))
200 .expect("failed to apply TLS config");
201 }
202
203 server_builder
204}
205
206pub async fn start_server<P: AsRef<Path>>(
208 db_path: P,
209 addr: &str,
210 shutdown_rx: oneshot::Receiver<()>,
211) -> Result<(), Box<dyn std::error::Error>> {
212 let options = ServerOptions {
213 listen_addr: addr.to_string(),
214 tls: None,
215 api_key: None,
216 storage: StorageOptions::default().db_path(db_path.as_ref()),
217 };
218 start_server_with_options(options, shutdown_rx).await
219}
220
221pub async fn start_server_with_options(
222 options: ServerOptions,
223 shutdown_rx: oneshot::Receiver<()>,
224) -> Result<(), Box<dyn std::error::Error>> {
225 let addr = options.listen_addr.parse()?;
226 let incoming = match TcpIncoming::bind(addr) {
228 Ok(incoming) => incoming,
229 Err(err) => {
230 return Err(Box::new(DcbError::InitializationError(format!(
231 "failed to bind to address {}: {}",
232 addr, err
233 ))));
234 }
235 }
236 .with_nodelay(Some(true))
237 .with_keepalive(Some(std::time::Duration::from_secs(60)));
238
239 let (srv_shutdown_tx, srv_shutdown_rx) = watch::channel(false);
241 let dcb_server = match DcbServer::new(srv_shutdown_rx, options.api_key.clone(), options.storage)
242 {
243 Ok(server) => server,
244 Err(err) => {
245 return Err(Box::new(err));
246 }
247 };
248
249 println!(
250 "UmaDB has {:?} events",
251 dcb_server
252 .request_handler
253 .head()
254 .unwrap_or(Some(0))
255 .unwrap_or(0)
256 );
257 let tls_mode_display_str = if options.tls.is_some() {
258 "with TLS"
259 } else {
260 "without TLS"
261 };
262
263 let api_key_display_str = if options.api_key.is_some() {
264 "with API key"
265 } else {
266 "without API key"
267 };
268
269 use tonic_health::ServingStatus; let (health_reporter, health_service) = tonic_health::server::health_reporter();
272 health_reporter
274 .set_service_status("", ServingStatus::Serving)
275 .await;
276 health_reporter
277 .set_service_status("umadb.v1.DCB", ServingStatus::Serving)
278 .await;
279 let health_reporter_for_shutdown = health_reporter.clone();
280
281 let mut builder = build_server_builder_with_options(options.tls)
283 .layer(PathRewriterLayer)
284 .add_service(health_service);
285
286 builder = builder.add_service(dcb_server.into_service());
288 let router = builder;
289
290 println!("UmaDB is listening on {addr} ({tls_mode_display_str}, {api_key_display_str})");
291 println!("UmaDB started in {:?}", uptime());
292 router
294 .serve_with_incoming_shutdown(incoming, async move {
295 let _ = shutdown_rx.await;
297 let _ = health_reporter_for_shutdown
299 .set_service_status("", ServingStatus::NotServing)
300 .await;
301 let _ = health_reporter_for_shutdown
302 .set_service_status("umadb.v1.DCB", ServingStatus::NotServing)
303 .await;
304 let _ = srv_shutdown_tx.send(true);
306 println!("\nUmaDB server shutdown complete");
307 })
308 .await?;
309
310 Ok(())
311}
312
313fn read_scan_concurrency_limit() -> usize {
325 const PER_CORE: usize = 4;
326 if let Some(n) = std::env::var("UMADB_READ_SCAN_CONCURRENCY")
327 .ok()
328 .and_then(|s| s.parse::<usize>().ok())
329 .filter(|n| *n > 0)
330 {
331 return n;
332 }
333 std::thread::available_parallelism()
334 .map(|n| n.get())
335 .unwrap_or(4)
336 .saturating_mul(PER_CORE)
337 .max(PER_CORE)
338}
339
340pub struct DcbServer {
342 pub(crate) request_handler: RequestHandler,
343 shutdown_watch_rx: watch::Receiver<bool>,
344 api_key: Option<String>,
345 read_scan_semaphore: Arc<Semaphore>,
347}
348
349impl DcbServer {
350 pub fn new(
351 shutdown_rx: watch::Receiver<bool>,
352 api_key: Option<String>,
353 storage_options: StorageOptions,
354 ) -> DcbResult<Self> {
355 let command_handler = RequestHandler::new(storage_options)?;
356 Ok(Self {
357 request_handler: command_handler,
358 shutdown_watch_rx: shutdown_rx,
359 api_key,
360 read_scan_semaphore: Arc::new(Semaphore::new(read_scan_concurrency_limit())),
361 })
362 }
363
364 pub fn into_service(self) -> umadb_proto::v1::dcb_server::DcbServer<Self> {
365 umadb_proto::v1::dcb_server::DcbServer::new(self)
366 }
367
368 fn enforce_api_key(&self, metadata: &tonic::metadata::MetadataMap) -> Result<(), Status> {
369 if let Some(expected) = &self.api_key {
370 let auth = metadata.get("authorization");
371 let expected_val = format!("Bearer {}", expected);
372 let ok = auth
373 .and_then(|m| m.to_str().ok())
374 .map(|s| s == expected_val)
375 .unwrap_or(false);
376 if !ok {
377 return Err(status_from_dcb_error(DcbError::AuthenticationError(
378 "missing or invalid API key".to_string(),
379 )));
380 }
381 }
382 Ok(())
383 }
384}
385
386#[tonic::async_trait]
387impl umadb_proto::v1::dcb_server::Dcb for DcbServer {
388 type ReadStream =
389 Pin<Box<dyn Stream<Item = Result<umadb_proto::v1::ReadResponse, Status>> + Send + 'static>>;
390 type SubscribeStream = Pin<
391 Box<dyn Stream<Item = Result<umadb_proto::v1::SubscribeResponse, Status>> + Send + 'static>,
392 >;
393
394 async fn read(
395 &self,
396 request: Request<umadb_proto::v1::ReadRequest>,
397 ) -> Result<Response<Self::ReadStream>, Status> {
398 self.enforce_api_key(request.metadata())?;
400 let read_request = request.into_inner();
401
402 #[allow(deprecated)]
404 if read_request.subscribe.unwrap_or(false) {
405 return Err(status_from_dcb_error(DcbError::InvalidArgument(
406 "The `subscribe` argument of `read()` has been deprecated. \
407 Please call the `subscribe()` method instead."
408 .to_string(),
409 )));
410 }
411
412 let query: Option<DcbQuery> = read_request.query.map(|q| q.into());
414 let start = read_request.start;
415 let backwards = read_request.backwards.unwrap_or(false);
416 let limit = read_request.limit;
417 let batch_size = read_request
419 .batch_size
420 .unwrap_or(READ_RESPONSE_BATCH_SIZE_DEFAULT)
421 .clamp(1, READ_RESPONSE_BATCH_SIZE_MAX);
422
423 let (tx, rx) = mpsc::channel(2048);
425 let request_handler = self.request_handler.clone();
427 let mut shutdown_watch_rx = self.shutdown_watch_rx.clone();
429
430 let cancel_signal = Arc::new(std::sync::atomic::AtomicBool::new(false));
431 let cancel_signal_for_task = cancel_signal.clone();
432 let read_scan_semaphore = self.read_scan_semaphore.clone();
433
434 tokio::spawn(async move {
436 let query_clone = query;
438 let mut next_start = start;
439 let mut sent_any = false;
440 let mut remaining_limit = limit.unwrap_or(u32::MAX);
441 let mut captured_db_head: Option<u64> = None;
442 let mut have_captured_db_head: bool = false;
443 loop {
444 if tx.is_closed() || *shutdown_watch_rx.borrow() {
448 cancel_signal_for_task.store(true, std::sync::atomic::Ordering::SeqCst);
449 break;
450 }
451 let read_limit = remaining_limit.min(batch_size);
453 if limit.is_some() && remaining_limit == 0 {
455 break;
456 }
457 let permit = match read_scan_semaphore.clone().acquire_owned().await {
461 Ok(permit) => permit,
462 Err(_) => break,
463 };
464 let handler = request_handler.clone();
465 let query_val = query_clone.clone();
466 let limit_val = Some(read_limit);
467 let cancel_for_blocking = cancel_signal_for_task.clone();
468 let mut blocking_handle = tokio::task::spawn_blocking(move || {
469 let _permit = permit;
470 handler.read(
471 query_val,
472 next_start,
473 backwards,
474 limit_val,
475 Some(cancel_for_blocking),
476 )
477 });
478
479 let res = tokio::select! {
480 res = &mut blocking_handle => {
481 res.map_err(|e| DcbError::InternalError(e.to_string())).and_then(|res| res)
482 }
483 _ = tx.closed() => {
484 cancel_signal_for_task.store(true, std::sync::atomic::Ordering::SeqCst);
485 let _ = blocking_handle.await;
487 break;
488 }
489 _ = shutdown_watch_rx.changed() => {
490 cancel_signal_for_task.store(true, std::sync::atomic::Ordering::SeqCst);
491 let _ = blocking_handle.await;
492 break;
493 }
494 };
495
496 match res {
497 Ok((dcb_sequenced_events, db_head)) => {
498 if !have_captured_db_head {
500 captured_db_head = db_head;
501 have_captured_db_head = true;
502 }
503
504 let original_len = dcb_sequenced_events.len();
506 let read_less_than_read_limit = (original_len as u32) < read_limit;
507
508 let sequenced_event_protos: Vec<umadb_proto::v1::SequencedEvent> =
510 dcb_sequenced_events
511 .into_iter()
512 .filter(|e| {
513 if let Some(h) = captured_db_head {
514 e.position <= h
515 } else {
516 true
517 }
518 })
519 .map(umadb_proto::v1::SequencedEvent::from)
520 .collect();
521
522 let reached_captured_head = captured_db_head.is_some()
524 && sequenced_event_protos.len() < original_len;
525
526 if sequenced_event_protos.is_empty() {
527 if !sent_any {
528 let response = umadb_proto::v1::ReadResponse {
530 events: vec![],
531 head: if limit.is_some() {
532 None
533 } else {
534 captured_db_head
535 },
536 };
537 let _ = tx.send(Ok(response)).await;
538 }
539 break;
541 }
542
543 let sent_count = sequenced_event_protos.len() as u32;
545
546 let last_event_position = sequenced_event_protos.last().map(|e| e.position);
547
548 let response = umadb_proto::v1::ReadResponse {
549 events: sequenced_event_protos,
550 head: if limit.is_some() {
551 last_event_position
552 } else {
553 captured_db_head
554 },
555 };
556
557 if tx.send(Ok(response)).await.is_err() {
558 break;
559 }
560 sent_any = true;
561
562 next_start = last_event_position.map(|p| {
564 if backwards {
565 p.saturating_sub(1)
566 } else {
567 p.saturating_add(1)
568 }
569 });
570
571 if read_less_than_read_limit || reached_captured_head {
574 break;
575 }
576
577 if limit.is_some() {
579 remaining_limit = remaining_limit.saturating_sub(sent_count);
580 if remaining_limit == 0 {
581 break;
582 }
583 }
584
585 tokio::task::yield_now().await;
587 }
588 Err(e) => {
589 if matches!(e, DcbError::CancelledByUser()) {
590 } else {
592 let _ = tx.send(Err(status_from_dcb_error(e))).await;
593 }
594 break;
595 }
596 }
597 }
598 });
599
600 Ok(Response::new(
602 Box::pin(ReceiverStream::new(rx)) as Self::ReadStream
603 ))
604 }
605
606 async fn subscribe(
607 &self,
608 request: Request<umadb_proto::v1::SubscribeRequest>,
609 ) -> Result<Response<Self::SubscribeStream>, Status> {
610 self.enforce_api_key(request.metadata())?;
612 let subscribe_request = request.into_inner();
613
614 let query: Option<DcbQuery> = subscribe_request.query.map(|q| q.into());
616 let after = subscribe_request.after;
617 let batch_size = subscribe_request
619 .batch_size
620 .unwrap_or(READ_RESPONSE_BATCH_SIZE_DEFAULT)
621 .clamp(1, READ_RESPONSE_BATCH_SIZE_MAX);
622
623 let (tx, rx) = mpsc::channel(2048);
625 let request_handler = self.request_handler.clone();
627 let mut shutdown_watch_rx = self.shutdown_watch_rx.clone();
629
630 let cancel_signal = Arc::new(std::sync::atomic::AtomicBool::new(false));
631 let cancel_signal_for_task = cancel_signal.clone();
632 let read_scan_semaphore = self.read_scan_semaphore.clone();
633
634 tokio::spawn(async move {
636 let query_clone = query;
638 let mut next_after = after.map(|a| a.saturating_add(1));
640 let mut head_rx = request_handler.watch_head();
642
643 loop {
644 if tx.is_closed() || *shutdown_watch_rx.borrow() {
648 cancel_signal_for_task.store(true, std::sync::atomic::Ordering::SeqCst);
649 break;
650 }
651
652 let permit = match read_scan_semaphore.clone().acquire_owned().await {
657 Ok(permit) => permit,
658 Err(_) => break,
659 };
660 let handler = request_handler.clone();
661 let query_val = query_clone.clone();
662 let batch_size_val = Some(batch_size);
663 let cancel_for_blocking = cancel_signal_for_task.clone();
664 let mut blocking_handle = tokio::task::spawn_blocking(move || {
665 let _permit = permit;
666 handler.read(
667 query_val,
668 next_after,
669 false,
670 batch_size_val,
671 Some(cancel_for_blocking),
672 )
673 });
674
675 let res = tokio::select! {
676 res = &mut blocking_handle => {
677 res.map_err(|e| DcbError::InternalError(e.to_string())).and_then(|res| res)
678 }
679 _ = tx.closed() => {
680 cancel_signal_for_task.store(true, std::sync::atomic::Ordering::SeqCst);
681 let _ = blocking_handle.await;
682 break;
683 }
684 _ = shutdown_watch_rx.changed() => {
685 cancel_signal_for_task.store(true, std::sync::atomic::Ordering::SeqCst);
686 let _ = blocking_handle.await;
687 break;
688 }
689 };
690
691 match res {
692 Ok((dcb_sequenced_events, _unused_db_head)) => {
693 let sequenced_event_protos: Vec<umadb_proto::v1::SequencedEvent> =
695 dcb_sequenced_events
696 .into_iter()
697 .map(umadb_proto::v1::SequencedEvent::from)
698 .collect();
699
700 if sequenced_event_protos.is_empty() {
701 tokio::select! {
703 _ = head_rx.changed() => {},
704 _ = shutdown_watch_rx.changed() => {},
705 _ = tx.closed() => {},
706 }
707 continue;
708 }
709
710 let last_event_position = sequenced_event_protos.last().map(|e| e.position);
711
712 let response = umadb_proto::v1::SubscribeResponse {
713 events: sequenced_event_protos,
714 };
715
716 if tx.send(Ok(response)).await.is_err() {
717 break;
718 }
719
720 next_after = last_event_position.map(|p| p.saturating_add(1));
723
724 tokio::task::yield_now().await;
726 }
727 Err(e) => {
728 if matches!(e, DcbError::CancelledByUser()) {
729 } else {
731 let _ = tx.send(Err(status_from_dcb_error(e))).await;
732 }
733 break;
734 }
735 }
736 }
737 });
738
739 Ok(Response::new(
741 Box::pin(ReceiverStream::new(rx)) as Self::SubscribeStream
742 ))
743 }
744
745 async fn append(
746 &self,
747 request: Request<umadb_proto::v1::AppendRequest>,
748 ) -> Result<Response<umadb_proto::v1::AppendResponse>, Status> {
749 self.enforce_api_key(request.metadata())?;
751 let req = request.into_inner();
752
753 let events: Vec<DcbEvent> = match req.events.into_iter().map(|e| e.try_into()).collect() {
755 Ok(events) => events,
756 Err(e) => {
757 return Err(status_from_dcb_error(e));
758 }
759 };
760 let condition = req.condition.map(|c| c.into());
761
762 let cancel_signal = Arc::new(std::sync::atomic::AtomicBool::new(false));
763 let cancel_signal_for_task = cancel_signal.clone();
764
765 let (cancel_tx, cancel_rx) = oneshot::channel();
767 let _guard = CancellationGuard(Some(cancel_tx));
768
769 let cancel_signal_for_monitoring = cancel_signal.clone();
771 tokio::spawn(async move {
772 let _ = cancel_rx.await;
775 cancel_signal_for_monitoring.store(true, std::sync::atomic::Ordering::SeqCst);
776 });
777
778 let res = self
780 .request_handler
781 .append(
782 events,
783 condition,
784 req.tracking_info.map(|t| TrackingInfo {
785 source: t.source,
786 position: t.position,
787 }),
788 Some(cancel_signal_for_task.clone()),
789 )
790 .await;
791
792 match res {
793 Ok(position) => Ok(Response::new(umadb_proto::v1::AppendResponse { position })),
794 Err(e) => Err(status_from_dcb_error(e)),
795 }
796 }
797
798 async fn head(
799 &self,
800 request: Request<umadb_proto::v1::HeadRequest>,
801 ) -> Result<Response<umadb_proto::v1::HeadResponse>, Status> {
802 self.enforce_api_key(request.metadata())?;
804 let request_handler = self.request_handler.clone();
808 let res = tokio::task::spawn_blocking(move || request_handler.head())
809 .await
810 .map_err(|e| status_from_dcb_error(DcbError::InternalError(e.to_string())))?;
811 match res {
812 Ok(position) => {
813 Ok(Response::new(umadb_proto::v1::HeadResponse { position }))
815 }
816 Err(e) => Err(status_from_dcb_error(e)),
817 }
818 }
819
820 async fn get_tracking_info(
821 &self,
822 request: Request<umadb_proto::v1::TrackingRequest>,
823 ) -> Result<Response<umadb_proto::v1::TrackingResponse>, Status> {
824 self.enforce_api_key(request.metadata())?;
826 let req = request.into_inner();
827 let permit = match self.read_scan_semaphore.clone().acquire_owned().await {
831 Ok(permit) => permit,
832 Err(_) => {
833 return Err(status_from_dcb_error(DcbError::InternalError(
834 "read-scan semaphore closed".to_string(),
835 )));
836 }
837 };
838 let request_handler = self.request_handler.clone();
839 let res = tokio::task::spawn_blocking(move || {
840 let _permit = permit;
841 request_handler.get_tracking_info(req.source)
842 })
843 .await
844 .map_err(|e| status_from_dcb_error(DcbError::InternalError(e.to_string())))?;
845 match res {
846 Ok(position) => Ok(Response::new(umadb_proto::v1::TrackingResponse {
847 position,
848 })),
849 Err(e) => Err(status_from_dcb_error(e)),
850 }
851 }
852}
853
854enum WriterRequest {
856 Append {
857 events: Vec<DcbEvent>,
858 condition: Option<DcbAppendCondition>,
859 tracking_info: Option<TrackingInfo>,
860 response_tx: oneshot::Sender<DcbResult<u64>>,
861 cancel: Option<Arc<std::sync::atomic::AtomicBool>>,
862 },
863 Shutdown,
864}
865
866struct RequestHandler {
868 mvcc: Arc<Mvcc>,
869 head_watch_tx: watch::Sender<Option<u64>>,
870 writer_request_tx: mpsc::Sender<WriterRequest>,
871}
872
873impl RequestHandler {
874 fn new(storage_options: StorageOptions) -> DcbResult<Self> {
875 let (request_tx, mut request_rx) = mpsc::channel::<WriterRequest>(1024);
877
878 let mvcc = Arc::new(Mvcc::new(false, storage_options)?);
880
881 let init_head = {
883 let header_page = mvcc.get_latest_header_page()?;
884 let header = header_page.as_header_node()?;
885 let last = header.next_position.0.saturating_sub(1);
886 if last == 0 { None } else { Some(last) }
887 };
888 let (head_tx, _head_rx) = watch::channel::<Option<u64>>(init_head);
889
890 let mvcc_for_writer = mvcc.clone();
892 let head_tx_writer = head_tx.clone();
893 thread::spawn(move || {
894 let db = UmaDb::from_arc(mvcc_for_writer);
895
896 let rt = Runtime::new().unwrap();
898
899 rt.block_on(async {
901 while let Some(request) = request_rx.recv().await {
902 match request {
903 WriterRequest::Append {
904 events,
905 condition,
906 tracking_info,
907 response_tx,
908 cancel,
909 } => {
910 let mut total_events = 0;
915 total_events += events.len();
916 let mvcc = &db.mvcc;
919 let mut writer = match mvcc.writer() {
920 Ok(writer) => writer,
921 Err(err) => {
922 let _ = response_tx.send(Err(err));
923 continue;
924 }
925 };
926
927 let mut responders: Vec<oneshot::Sender<DcbResult<u64>>> = Vec::new();
928 let mut results: Vec<DcbResult<u64>> = Vec::new();
929
930 let mut abort_idx: Option<usize> = None;
932 let mut abort_err: Option<DcbError> = None;
933
934 responders.push(response_tx);
935 let result = UmaDb::process_append_request(
936 events,
937 condition,
938 tracking_info,
939 mvcc,
940 &mut writer,
941 cancel,
942 );
943 match &result {
945 Ok(_) => results.push(result),
946 Err(e) if is_integrity_error(e) => {
947 results.push(Err(clone_dcb_error(e)))
948 }
949 Err(e) if is_invalid_argument_error(e) => {
950 results.push(Err(clone_dcb_error(e)))
951 }
952 Err(e) => {
953 abort_idx = Some(0);
954 abort_err = Some(clone_dcb_error(e));
955 results.push(Err(clone_dcb_error(e)));
956 }
957 }
958
959 loop {
963 if total_events >= APPEND_BATCH_MAX_EVENTS {
964 break;
965 }
966 if abort_idx.is_some() {
968 break;
969 }
970 match request_rx.try_recv() {
971 Ok(WriterRequest::Append {
972 events,
973 condition,
974 tracking_info,
975 response_tx,
976 cancel,
977 }) => {
978 let ev_len = events.len();
979 let idx_in_batch = responders.len();
980 responders.push(response_tx);
981 let res_next = UmaDb::process_append_request(
982 events,
983 condition,
984 tracking_info,
985 mvcc,
986 &mut writer,
987 cancel,
988 );
989 match &res_next {
990 Ok(_) => results.push(res_next),
991 Err(e) if is_integrity_error(e) => {
992 results.push(Err(clone_dcb_error(e)))
993 }
994 Err(e) if is_invalid_argument_error(e) => {
995 results.push(Err(clone_dcb_error(e)))
996 }
997 Err(e) => {
998 abort_idx = Some(idx_in_batch);
999 abort_err = Some(clone_dcb_error(e));
1000 results.push(Err(clone_dcb_error(e)));
1001 }
1003 }
1004 total_events += ev_len;
1005 }
1006 Ok(WriterRequest::Shutdown) => {
1007 break;
1012 }
1013 Err(mpsc::error::TryRecvError::Empty) => {
1014 break;
1015 }
1016 Err(mpsc::error::TryRecvError::Disconnected) => break,
1017 }
1018 }
1019 if let (Some(failed_at), Some(orig_err)) = (abort_idx, abort_err) {
1022 let shadow = shadow_for_batch_abort(&orig_err);
1024 for (i, tx) in responders.into_iter().enumerate() {
1025 if i == failed_at {
1026 let _ = tx.send(Err(clone_dcb_error(&orig_err)));
1027 } else {
1028 let _ = tx.send(Err(clone_dcb_error(&shadow)));
1029 }
1030 }
1031 continue;
1033 }
1034
1035 let batch_result = match mvcc.commit(&mut writer) {
1037 Ok(_) => Ok(results),
1038 Err(err) => Err(err),
1039 };
1040
1041 match batch_result {
1042 Ok(results) => {
1043 for (res, tx) in results.into_iter().zip(responders.into_iter())
1045 {
1046 let _ = tx.send(res);
1047 }
1048 let last_committed = writer.next_position.0.saturating_sub(1);
1050 let new_head = if last_committed == 0 {
1051 None
1052 } else {
1053 Some(last_committed)
1054 };
1055 let _ = head_tx_writer.send(new_head);
1056 }
1057 Err(e) => {
1058 let total = responders.len();
1062 let mut iter = responders.into_iter();
1063 for _ in 0..total {
1064 if let Some(tx) = iter.next() {
1065 let _ = tx.send(Err(clone_dcb_error(&e)));
1066 }
1067 }
1068 }
1069 }
1070 }
1071 WriterRequest::Shutdown => {
1072 break;
1073 }
1074 }
1075 }
1076 });
1077 });
1078
1079 Ok(Self {
1080 mvcc,
1081 head_watch_tx: head_tx,
1082 writer_request_tx: request_tx,
1083 })
1084 }
1085
1086 fn read(
1087 &self,
1088 query: Option<DcbQuery>,
1089 start: Option<u64>,
1090 backwards: bool,
1091 limit: Option<u32>,
1092 cancel: Option<Arc<std::sync::atomic::AtomicBool>>,
1093 ) -> DcbResult<(Vec<DcbSequencedEvent>, Option<u64>)> {
1094 let reader = self.mvcc.reader()?;
1095 let db_head = if reader.next_position > Position(1) {
1096 Some(reader.next_position.0.saturating_sub(1))
1097 } else {
1098 None
1099 };
1100
1101 let q = query.unwrap_or(DcbQuery { items: vec![] });
1102 let start_position = start.map(Position);
1103
1104 let events = read_conditional(
1105 &self.mvcc,
1106 &std::collections::HashMap::new(),
1107 reader.events_tree_root_id,
1108 reader.tags_tree_root_id,
1109 q,
1110 start_position,
1111 backwards,
1112 limit,
1113 false,
1114 cancel,
1115 )
1116 .map_err(|e| match e {
1117 DcbError::CancelledByUser() => DcbError::CancelledByUser(),
1118 _ => DcbError::Corruption(format!("{e}")),
1119 })?;
1120
1121 Ok((events, db_head))
1122 }
1123
1124 fn head(&self) -> DcbResult<Option<u64>> {
1125 let header_page = self
1126 .mvcc
1127 .get_latest_header_page()
1128 .map_err(|e| DcbError::Corruption(format!("{e}")))?;
1129 let header = header_page
1130 .as_header_node()
1131 .map_err(|e| DcbError::Corruption(format!("{e}")))?;
1132 let last = header.next_position.0.saturating_sub(1);
1133 if last == 0 { Ok(None) } else { Ok(Some(last)) }
1134 }
1135
1136 fn get_tracking_info(&self, source: String) -> DcbResult<Option<u64>> {
1137 let db = UmaDb::from_arc(self.mvcc.clone());
1138 db.get_tracking_info(&source)
1139 }
1140
1141 pub async fn append(
1142 &self,
1143 events: Vec<DcbEvent>,
1144 condition: Option<DcbAppendCondition>,
1145 tracking_info: Option<TrackingInfo>,
1146 cancel: Option<Arc<std::sync::atomic::AtomicBool>>,
1147 ) -> DcbResult<u64> {
1148 let (response_tx, response_rx) = oneshot::channel();
1160
1161 self.writer_request_tx
1162 .send(WriterRequest::Append {
1163 events,
1164 condition,
1165 tracking_info,
1166 response_tx,
1167 cancel,
1168 })
1169 .await
1170 .map_err(|_| {
1171 DcbError::Io(std::io::Error::other(
1172 "failed to send append request to EventStore thread",
1173 ))
1174 })?;
1175
1176 response_rx.await.map_err(|_| {
1177 DcbError::Io(std::io::Error::other(
1178 "failed to receive append response from EventStore thread",
1179 ))
1180 })?
1181 }
1182
1183 fn watch_head(&self) -> watch::Receiver<Option<u64>> {
1184 self.head_watch_tx.subscribe()
1185 }
1186
1187 #[allow(dead_code)]
1188 async fn shutdown(&self) {
1189 let _ = self.writer_request_tx.send(WriterRequest::Shutdown).await;
1190 }
1191}
1192
1193impl Clone for RequestHandler {
1195 fn clone(&self) -> Self {
1196 Self {
1197 mvcc: self.mvcc.clone(),
1198 head_watch_tx: self.head_watch_tx.clone(),
1199 writer_request_tx: self.writer_request_tx.clone(),
1200 }
1201 }
1202}