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