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) = mvcc.get_latest_header()?;
802 let last = header.next_position.0.saturating_sub(1);
803 if last == 0 { None } else { Some(last) }
804 };
805 let (head_tx, _head_rx) = watch::channel::<Option<u64>>(init_head);
806
807 let mvcc_for_writer = mvcc.clone();
809 let head_tx_writer = head_tx.clone();
810 thread::spawn(move || {
811 let db = UmaDb::from_arc(mvcc_for_writer);
812
813 let rt = Runtime::new().unwrap();
815
816 rt.block_on(async {
818 while let Some(request) = request_rx.recv().await {
819 match request {
820 WriterRequest::Append {
821 events,
822 condition,
823 tracking_info,
824 response_tx,
825 } => {
826 let mut total_events = 0;
831 total_events += events.len();
832 let mvcc = &db.mvcc;
835 let mut writer = match mvcc.writer() {
836 Ok(writer) => writer,
837 Err(err) => {
838 let _ = response_tx.send(Err(err));
839 continue;
840 }
841 };
842
843 let mut responders: Vec<oneshot::Sender<DcbResult<u64>>> = Vec::new();
844 let mut results: Vec<DcbResult<u64>> = Vec::new();
845
846 let mut abort_idx: Option<usize> = None;
848 let mut abort_err: Option<DcbError> = None;
849
850 responders.push(response_tx);
851 let result = UmaDb::process_append_request(
852 events,
853 condition,
854 tracking_info,
855 mvcc,
856 &mut writer,
857 );
858 match &result {
860 Ok(_) => results.push(result),
861 Err(e) if is_integrity_error(e) => {
862 results.push(Err(clone_dcb_error(e)))
863 }
864 Err(e) => {
865 abort_idx = Some(0);
866 abort_err = Some(clone_dcb_error(e));
867 results.push(Err(clone_dcb_error(e)));
868 }
869 }
870
871 loop {
875 if total_events >= APPEND_BATCH_MAX_EVENTS {
876 break;
877 }
878 if abort_idx.is_some() {
880 break;
881 }
882 match request_rx.try_recv() {
883 Ok(WriterRequest::Append {
884 events,
885 condition,
886 tracking_info,
887 response_tx,
888 }) => {
889 let ev_len = events.len();
890 let idx_in_batch = responders.len();
891 responders.push(response_tx);
892 let res_next = UmaDb::process_append_request(
893 events,
894 condition,
895 tracking_info,
896 mvcc,
897 &mut writer,
898 );
899 match &res_next {
900 Ok(_) => results.push(res_next),
901 Err(e) if is_integrity_error(e) => {
902 results.push(Err(clone_dcb_error(e)))
903 }
904 Err(e) => {
905 abort_idx = Some(idx_in_batch);
906 abort_err = Some(clone_dcb_error(e));
907 results.push(Err(clone_dcb_error(e)));
908 }
910 }
911 total_events += ev_len;
912 }
913 Ok(WriterRequest::Shutdown) => {
914 break;
919 }
920 Err(mpsc::error::TryRecvError::Empty) => {
921 break;
922 }
923 Err(mpsc::error::TryRecvError::Disconnected) => break,
924 }
925 }
926 if let (Some(failed_at), Some(orig_err)) = (abort_idx, abort_err) {
929 let shadow = shadow_for_batch_abort(&orig_err);
931 for (i, tx) in responders.into_iter().enumerate() {
932 if i == failed_at {
933 let _ = tx.send(Err(clone_dcb_error(&orig_err)));
934 } else {
935 let _ = tx.send(Err(clone_dcb_error(&shadow)));
936 }
937 }
938 continue;
940 }
941
942 let batch_result = match mvcc.commit(&mut writer) {
944 Ok(_) => Ok(results),
945 Err(err) => Err(err),
946 };
947
948 match batch_result {
949 Ok(results) => {
950 for (res, tx) in results.into_iter().zip(responders.into_iter())
952 {
953 let _ = tx.send(res);
954 }
955 let last_committed = writer.next_position.0.saturating_sub(1);
957 let new_head = if last_committed == 0 {
958 None
959 } else {
960 Some(last_committed)
961 };
962 let _ = head_tx_writer.send(new_head);
963 }
964 Err(e) => {
965 let total = responders.len();
969 let mut iter = responders.into_iter();
970 for _ in 0..total {
971 if let Some(tx) = iter.next() {
972 let _ = tx.send(Err(clone_dcb_error(&e)));
973 }
974 }
975 }
976 }
977 }
978 WriterRequest::Shutdown => {
979 break;
980 }
981 }
982 }
983 });
984 });
985
986 Ok(Self {
987 mvcc,
988 head_watch_tx: head_tx,
989 writer_request_tx: request_tx,
990 })
991 }
992
993 fn read(
994 &self,
995 query: Option<DcbQuery>,
996 start: Option<u64>,
997 backwards: bool,
998 limit: Option<u32>,
999 ) -> DcbResult<(Vec<DcbSequencedEvent>, Option<u64>)> {
1000 let reader = self.mvcc.reader()?;
1001 let last_committed_position = reader.next_position.0.saturating_sub(1);
1002
1003 let q = query.unwrap_or(DcbQuery { items: vec![] });
1004 let start_position = start.map(Position);
1005
1006 let events = read_conditional(
1007 &self.mvcc,
1008 &std::collections::HashMap::new(),
1009 reader.events_tree_root_id,
1010 reader.tags_tree_root_id,
1011 q,
1012 start_position,
1013 backwards,
1014 limit,
1015 false,
1016 )
1017 .map_err(|e| DcbError::Corruption(format!("{e}")))?;
1018
1019 let head = if limit.is_none() {
1020 if last_committed_position == 0 {
1021 None
1022 } else {
1023 Some(last_committed_position)
1024 }
1025 } else {
1026 events.last().map(|e| e.position)
1027 };
1028
1029 Ok((events, head))
1030 }
1031
1032 fn head(&self) -> DcbResult<Option<u64>> {
1033 let (_, header) = self
1034 .mvcc
1035 .get_latest_header()
1036 .map_err(|e| DcbError::Corruption(format!("{e}")))?;
1037 let last = header.next_position.0.saturating_sub(1);
1038 if last == 0 { Ok(None) } else { Ok(Some(last)) }
1039 }
1040
1041 fn get_tracking_info(&self, source: String) -> DcbResult<Option<u64>> {
1042 let db = UmaDb::from_arc(self.mvcc.clone());
1043 db.get_tracking_info(&source)
1044 }
1045
1046 pub async fn append(
1047 &self,
1048 events: Vec<DcbEvent>,
1049 condition: Option<DcbAppendCondition>,
1050 tracking_info: Option<TrackingInfo>,
1051 ) -> DcbResult<u64> {
1052 let pre_append_decision = if let Some(mut given_condition) = condition {
1054 let reader = self.mvcc.reader()?;
1055 let current_head = {
1056 let last = reader.next_position.0.saturating_sub(1);
1057 if last == 0 { None } else { Some(last) }
1058 };
1059
1060 let from = given_condition.after.map(|after| Position(after + 1));
1062 let empty_dirty = std::collections::HashMap::new();
1063 let found = read_conditional(
1064 &self.mvcc,
1065 &empty_dirty,
1066 reader.events_tree_root_id,
1067 reader.tags_tree_root_id,
1068 given_condition.fail_if_events_match.clone(),
1069 from,
1070 false,
1071 Some(1),
1072 false,
1073 )?;
1074
1075 if let Some(matched) = found.first() {
1076 match is_request_idempotent(
1078 &self.mvcc,
1079 &empty_dirty,
1080 reader.events_tree_root_id,
1081 reader.tags_tree_root_id,
1082 &events,
1083 given_condition.fail_if_events_match.clone(),
1084 from,
1085 ) {
1086 Ok(Some(last_recorded_position)) => {
1087 PreAppendDecision::AlreadyAppended(last_recorded_position)
1089 }
1090 Ok(None) => {
1091 let msg = format!(
1093 "condition: {:?} matched: {:?}",
1094 given_condition.clone(),
1095 matched,
1096 );
1097 return Err(DcbError::IntegrityError(msg));
1098 }
1099 Err(err) => {
1100 return Err(err);
1102 }
1103 }
1104 } else {
1105 let new_after = std::cmp::max(
1107 given_condition.after.unwrap_or(0),
1108 current_head.unwrap_or(0),
1109 );
1110 given_condition.after = Some(new_after);
1111
1112 PreAppendDecision::UseCondition(Some(given_condition))
1113 }
1114 } else {
1115 PreAppendDecision::UseCondition(None)
1117 };
1118
1119 match pre_append_decision {
1121 PreAppendDecision::AlreadyAppended(last_found_position) => {
1122 Ok(last_found_position)
1124 }
1125 PreAppendDecision::UseCondition(adjusted_condition) => {
1126 let (response_tx, response_rx) = oneshot::channel();
1128
1129 self.writer_request_tx
1130 .send(WriterRequest::Append {
1131 events,
1132 condition: adjusted_condition,
1133 tracking_info,
1134 response_tx,
1135 })
1136 .await
1137 .map_err(|_| {
1138 DcbError::Io(std::io::Error::other(
1139 "failed to send append request to EventStore thread",
1140 ))
1141 })?;
1142
1143 response_rx.await.map_err(|_| {
1144 DcbError::Io(std::io::Error::other(
1145 "failed to receive append response from EventStore thread",
1146 ))
1147 })?
1148 }
1149 }
1150 }
1151
1152 fn watch_head(&self) -> watch::Receiver<Option<u64>> {
1153 self.head_watch_tx.subscribe()
1154 }
1155
1156 #[allow(dead_code)]
1157 async fn shutdown(&self) {
1158 let _ = self.writer_request_tx.send(WriterRequest::Shutdown).await;
1159 }
1160}
1161
1162impl Clone for RequestHandler {
1164 fn clone(&self) -> Self {
1165 Self {
1166 mvcc: self.mvcc.clone(),
1167 head_watch_tx: self.head_watch_tx.clone(),
1168 writer_request_tx: self.writer_request_tx.clone(),
1169 }
1170 }
1171}
1172
1173#[derive(Debug)]
1174enum PreAppendDecision {
1175 UseCondition(Option<DcbAppendCondition>),
1177 AlreadyAppended(u64),
1179}