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