1use std::{
5 ops::Deref,
6 sync::{
7 Arc,
8 atomic::{AtomicBool, Ordering},
9 },
10};
11
12use reifydb_auth::service::AuthEngine;
13use reifydb_catalog::{
14 catalog::Catalog,
15 interceptor::CatalogCacheInterceptor,
16 vtable::{
17 system::flow_operator_store::{SystemFlowOperatorEventListener, SystemFlowOperatorStore},
18 tables::UserVTableDataFunction,
19 user::{UserVTable, UserVTableColumn, registry::UserVTableEntry},
20 },
21};
22use reifydb_cdc::{
23 consume::{host::CdcHost, wake::CdcWakeRegistry, watermark::CdcConsumerWatermark},
24 produce::watermark::CdcProducerWatermark,
25 storage::CdcStore,
26};
27use reifydb_core::{
28 common::CommitVersion,
29 error::diagnostic::engine::read_only_rejection,
30 event::{Event, EventBus},
31 execution::ExecutionResult,
32 interface::{
33 WithEventBus,
34 catalog::{
35 column::{Column, ColumnIndex},
36 id::{ColumnId, NamespaceId},
37 vtable::{VTable, VTableId},
38 },
39 },
40 util::ioc::IocContainer,
41};
42use reifydb_metric::storage::metric::MetricReader;
43use reifydb_runtime::{
44 actor::{mailbox::ActorRef, system::ActorSpawner},
45 context::{clock::Clock, rng::Rng},
46 shutdown::Shutdown,
47 version_epoch::VersionEpoch,
48};
49use reifydb_store_single::SingleStore;
50use reifydb_transaction::{
51 dictionary::DictionaryAllocatorRegistry,
52 error::TransactionError,
53 interceptor::{factory::InterceptorFactory, interceptors::Interceptors},
54 multi::{lease::VersionLeaseGuard, transaction::MultiTransaction},
55 single::SingleTransaction,
56 transaction::{admin::AdminTransaction, command::CommandTransaction, query::QueryTransaction},
57};
58use reifydb_value::{
59 error::Error,
60 fragment::Fragment,
61 params::Params,
62 reifydb_assertions,
63 value::{constraint::TypeConstraint, duration::Duration, identity::IdentityId},
64};
65use tracing::instrument;
66
67use crate::{
68 Result,
69 bulk_insert::builder::{BulkInsertBuilder, Unchecked, Validated},
70 vm::{
71 Admin, Command, Query, Subscription,
72 executor::Executor,
73 flow_lineage::ViewLineage,
74 services::{EngineConfig, Services},
75 },
76};
77
78pub struct StandardEngine(Arc<Inner>);
79
80impl WithEventBus for StandardEngine {
81 fn event_bus(&self) -> &EventBus {
82 &self.event_bus
83 }
84}
85
86impl AuthEngine for StandardEngine {
87 fn begin_admin(&self) -> Result<AdminTransaction> {
88 StandardEngine::begin_admin(self, IdentityId::system())
89 }
90
91 fn begin_query(&self) -> Result<QueryTransaction> {
92 StandardEngine::begin_query(self, IdentityId::system())
93 }
94
95 fn catalog(&self) -> Catalog {
96 StandardEngine::catalog(self)
97 }
98}
99
100impl StandardEngine {
101 #[instrument(name = "engine::transaction::begin_command", level = "debug", skip(self))]
102 pub fn begin_command(&self, identity: IdentityId) -> Result<CommandTransaction> {
103 reifydb_assertions! {
104 assert!(
105 !self.is_read_only(),
106 "begin_command called on a read-only engine: writes are permanently disabled after set_read_only(), so any caller reaching this point has bypassed the reject_if_read_only guard (identity={:?})",
107 identity
108 );
109 }
110 let interceptors = self.interceptors.create();
111 let mut txn = CommandTransaction::new(
112 self.multi.clone(),
113 self.single.clone(),
114 self.event_bus.clone(),
115 interceptors,
116 identity,
117 self.executor.runtime_context.clock.clone(),
118 )?;
119 txn.set_executor(Arc::new(self.executor.clone()));
120 txn.set_dictionary_allocators(self.dictionary_allocators.clone());
121 Ok(txn)
122 }
123
124 #[instrument(name = "engine::transaction::begin_admin", level = "debug", skip(self))]
125 pub fn begin_admin(&self, identity: IdentityId) -> Result<AdminTransaction> {
126 let interceptors = self.interceptors.create();
127 let mut txn = AdminTransaction::new(
128 self.multi.clone(),
129 self.single.clone(),
130 self.event_bus.clone(),
131 interceptors,
132 identity,
133 self.executor.runtime_context.clock.clone(),
134 )?;
135 txn.set_executor(Arc::new(self.executor.clone()));
136 txn.set_dictionary_allocators(self.dictionary_allocators.clone());
137 Ok(txn)
138 }
139
140 #[instrument(name = "engine::transaction::begin_query", level = "trace", skip(self))]
141 pub fn begin_query(&self, identity: IdentityId) -> Result<QueryTransaction> {
142 let mut txn = QueryTransaction::new(self.multi.begin_query()?, self.single.clone(), identity);
143 txn.set_executor(Arc::new(self.executor.clone()));
144 Ok(txn)
145 }
146
147 pub fn clock(&self) -> &Clock {
148 &self.executor.runtime_context.clock
149 }
150
151 pub fn rng(&self) -> &Rng {
152 &self.executor.runtime_context.rng
153 }
154
155 pub fn version_epoch(&self) -> &VersionEpoch {
156 &self.executor.runtime_context.version_epoch
157 }
158
159 #[instrument(name = "engine::admin_as", level = "debug", skip(self, params), fields(rql = %rql))]
160 pub fn admin_as(&self, identity: IdentityId, rql: &str, params: Params) -> ExecutionResult {
161 if let Some(e) = self.reject_request(identity) {
162 return ExecutionResult::from_error(e);
163 }
164 let mut txn = match self.begin_admin(identity) {
165 Ok(t) => t,
166 Err(mut e) => {
167 e.with_rql(rql.to_string());
168 return ExecutionResult::from_error(e);
169 }
170 };
171 let mut outcome = self.executor.admin(
172 &mut txn,
173 Admin {
174 rql,
175 params,
176 },
177 );
178 self.commit_admin(&mut txn, &mut outcome, rql);
179 self.annotate_rql(&mut outcome, rql);
180 outcome
181 }
182
183 fn reject_request(&self, identity: IdentityId) -> Option<Error> {
184 if let Err(e) = self.reject_if_read_only() {
185 return Some(e);
186 }
187 if let Err(e) = self.reject_if_shutting_down(identity) {
188 return Some(e);
189 }
190 None
191 }
192
193 #[inline]
194 fn commit_admin(&self, txn: &mut AdminTransaction, outcome: &mut ExecutionResult, rql: &str) {
195 if outcome.is_ok()
196 && let Err(mut e) = txn.commit()
197 {
198 e.with_rql(rql.to_string());
199 outcome.error = Some(e);
200 }
201 }
202
203 fn annotate_rql(&self, outcome: &mut ExecutionResult, rql: &str) {
204 if let Some(ref mut e) = outcome.error {
205 e.with_rql(rql.to_string());
206 }
207 reifydb_assertions! {
208 let annotated = outcome.error.as_ref().map(|e| e.rql.is_some());
209 assert!(
210 annotated != Some(false),
211 "annotate_rql is the single catch-all that attaches the originating query to every error leaving admin_as/command_as; an error reaching the user with rql=None (annotated={:?}) would render a diagnostic with no source query, defeating user-facing error reporting",
212 annotated
213 );
214 }
215 }
216
217 #[instrument(name = "engine::command_as", level = "debug", skip(self, params), fields(rql = %rql))]
218 pub fn command_as(&self, identity: IdentityId, rql: &str, params: Params) -> ExecutionResult {
219 if let Some(e) = self.reject_request(identity) {
220 return ExecutionResult::from_error(e);
221 }
222 let mut txn = match self.begin_command(identity) {
223 Ok(t) => t,
224 Err(mut e) => {
225 e.with_rql(rql.to_string());
226 return ExecutionResult::from_error(e);
227 }
228 };
229 let mut outcome = self.executor.command(
230 &mut txn,
231 Command {
232 rql,
233 params,
234 },
235 );
236 self.commit_command(&mut txn, &mut outcome, rql);
237 self.annotate_rql(&mut outcome, rql);
238 outcome
239 }
240
241 #[inline]
242 fn commit_command(&self, txn: &mut CommandTransaction, outcome: &mut ExecutionResult, rql: &str) {
243 if outcome.is_ok()
244 && let Err(mut e) = txn.commit()
245 {
246 e.with_rql(rql.to_string());
247 outcome.error = Some(e);
248 }
249 }
250
251 #[instrument(name = "engine::query_as", level = "debug", skip(self, params), fields(rql = %rql))]
252 pub fn query_as(&self, identity: IdentityId, rql: &str, params: Params) -> ExecutionResult {
253 let mut txn = match self.begin_query(identity) {
254 Ok(t) => t,
255 Err(mut e) => {
256 e.with_rql(rql.to_string());
257 return ExecutionResult::from_error(e);
258 }
259 };
260 let mut outcome = self.executor.query(
261 &mut txn,
262 Query {
263 rql,
264 params,
265 },
266 );
267 if let Some(ref mut e) = outcome.error {
268 e.with_rql(rql.to_string());
269 }
270 outcome
271 }
272
273 #[instrument(name = "engine::query_as_at_version", level = "debug", skip(self, params, lease), fields(rql = %rql, version = %lease.version().0))]
274 pub fn query_as_at_version(
275 &self,
276 identity: IdentityId,
277 rql: &str,
278 params: Params,
279 lease: &VersionLeaseGuard,
280 ) -> ExecutionResult {
281 let mut txn = match self.begin_query_at_version(lease, identity) {
282 Ok(t) => t,
283 Err(mut e) => {
284 e.with_rql(rql.to_string());
285 return ExecutionResult::from_error(e);
286 }
287 };
288 let mut outcome = self.executor.query(
289 &mut txn,
290 Query {
291 rql,
292 params,
293 },
294 );
295 if let Some(ref mut e) = outcome.error {
296 e.with_rql(rql.to_string());
297 }
298 outcome
299 }
300
301 #[instrument(name = "engine::query_in_txn", level = "debug", skip(self, txn, params), fields(rql = %rql))]
302 pub fn query_in_txn(&self, txn: &mut QueryTransaction, rql: &str, params: Params) -> ExecutionResult {
303 let mut outcome = self.executor.query(
304 txn,
305 Query {
306 rql,
307 params,
308 },
309 );
310 if let Some(ref mut e) = outcome.error {
311 e.with_rql(rql.to_string());
312 }
313 outcome
314 }
315
316 #[instrument(name = "engine::subscribe_as", level = "debug", skip(self, params), fields(rql = %rql))]
317 pub fn subscribe_as(&self, identity: IdentityId, rql: &str, params: Params) -> ExecutionResult {
318 let mut txn = match self.begin_query(identity) {
319 Ok(t) => t,
320 Err(mut e) => {
321 e.with_rql(rql.to_string());
322 return ExecutionResult::from_error(e);
323 }
324 };
325 let mut outcome = self.executor.subscription(
326 &mut txn,
327 Subscription {
328 rql,
329 params,
330 },
331 );
332 if let Some(ref mut e) = outcome.error {
333 e.with_rql(rql.to_string());
334 }
335 outcome
336 }
337
338 #[instrument(name = "engine::procedure_as", level = "debug", skip(self, params), fields(name = %name))]
339 pub fn procedure_as(&self, identity: IdentityId, name: &str, params: Params) -> ExecutionResult {
340 if let Err(e) = self.reject_if_read_only() {
341 return ExecutionResult::from_error(e);
342 }
343 if let Err(e) = self.reject_if_shutting_down(identity) {
344 return ExecutionResult::from_error(e);
345 }
346 let mut txn = match self.begin_command(identity) {
347 Ok(t) => t,
348 Err(e) => {
349 return ExecutionResult::from_error(e);
350 }
351 };
352 let mut outcome = self.executor.call_procedure(&mut txn, name, ¶ms);
353 if outcome.is_ok()
354 && let Err(e) = txn.commit()
355 {
356 outcome.error = Some(e);
357 }
358 outcome
359 }
360
361 pub fn register_virtual_table<T: UserVTable>(
362 &self,
363 namespace_id: NamespaceId,
364 name: &str,
365 table: T,
366 ) -> Result<VTableId> {
367 let catalog = self.catalog();
368 let table_id = self.executor.virtual_table_registry.allocate_id();
369
370 let table_columns = table.vtable();
371 let columns = convert_vtable_user_columns_to_columns(&table_columns);
372
373 let def = Arc::new(VTable {
374 id: table_id,
375 namespace: namespace_id,
376 name: name.to_string(),
377 columns,
378 });
379
380 catalog.register_vtable_user(def.clone())?;
381
382 let data_fn: UserVTableDataFunction = Arc::new(move |_params| table.get());
383
384 let entry = UserVTableEntry {
385 def: def.clone(),
386 data_fn,
387 };
388 self.executor.virtual_table_registry.register(namespace_id, name.to_string(), entry);
389 Ok(table_id)
390 }
391}
392
393impl CdcHost for StandardEngine {
394 fn begin_command(&self) -> Result<CommandTransaction> {
395 StandardEngine::begin_command(self, IdentityId::system())
396 }
397
398 fn begin_query(&self) -> Result<QueryTransaction> {
399 StandardEngine::begin_query(self, IdentityId::system())
400 }
401
402 fn current_version(&self) -> Result<CommitVersion> {
403 StandardEngine::current_version(self)
404 }
405
406 fn done_until(&self) -> CommitVersion {
407 StandardEngine::done_until(self)
408 }
409
410 fn cdc_producer_watermark(&self) -> CommitVersion {
411 StandardEngine::cdc_producer_watermark(self)
412 }
413
414 fn wait_for_mark_timeout(&self, version: CommitVersion, timeout: Duration) -> bool {
415 StandardEngine::wait_for_mark_timeout(self, version, timeout)
416 }
417
418 fn notify_on_mark(&self, version: CommitVersion, callback: Box<dyn FnOnce() + Send>) {
419 StandardEngine::notify_on_mark(self, version, callback);
420 }
421
422 fn catalog(&self) -> &Catalog {
423 &self.catalog
424 }
425}
426
427impl Clone for StandardEngine {
428 fn clone(&self) -> Self {
429 Self(self.0.clone())
430 }
431}
432
433impl Deref for StandardEngine {
434 type Target = Inner;
435
436 fn deref(&self) -> &Self::Target {
437 &self.0
438 }
439}
440
441pub struct Inner {
442 multi: MultiTransaction,
443 single: SingleTransaction,
444 event_bus: EventBus,
445 executor: Executor,
446 interceptors: Arc<InterceptorFactory>,
447 catalog: Catalog,
448 flow_operator_store: SystemFlowOperatorStore,
449 dictionary_allocators: DictionaryAllocatorRegistry,
450 read_only: AtomicBool,
451 shutting_down: AtomicBool,
452}
453
454impl StandardEngine {
455 pub fn new(
456 multi: MultiTransaction,
457 single: SingleTransaction,
458 event_bus: EventBus,
459 interceptors: InterceptorFactory,
460 catalog: Catalog,
461 config: EngineConfig,
462 ) -> Self {
463 let flow_operator_store = SystemFlowOperatorStore::new();
464 let listener = SystemFlowOperatorEventListener::new(flow_operator_store.clone());
465 event_bus.register(listener);
466
467 let metrics_store = config
468 .ioc
469 .resolve::<SingleStore>()
470 .expect("SingleStore must be registered in IocContainer for metrics");
471 let stats_reader = MetricReader::new(metrics_store);
472
473 let catalog_for_interceptor = catalog.clone();
474 interceptors.add_late(Arc::new(move |interceptors: &mut Interceptors| {
475 interceptors.post_commit.add(Arc::new(CatalogCacheInterceptor::new(&catalog_for_interceptor)));
476 }));
477
478 let interceptors = Arc::new(interceptors);
479
480 Self(Arc::new(Inner {
481 multi,
482 single,
483 event_bus,
484 executor: Executor::new(catalog.clone(), config, flow_operator_store.clone(), stats_reader),
485 interceptors,
486 catalog,
487 flow_operator_store,
488 dictionary_allocators: DictionaryAllocatorRegistry::new(),
489 read_only: AtomicBool::new(false),
490 shutting_down: AtomicBool::new(false),
491 }))
492 }
493
494 pub fn create_interceptors(&self) -> Interceptors {
495 self.interceptors.create()
496 }
497
498 pub fn dictionary_allocators(&self) -> DictionaryAllocatorRegistry {
499 self.dictionary_allocators.clone()
500 }
501
502 pub fn add_interceptor_factory(&self, factory: Arc<dyn Fn(&mut Interceptors) + Send + Sync>) {
503 self.interceptors.add_late(factory);
504 }
505
506 #[instrument(name = "engine::transaction::begin_query_at_version", level = "trace", skip(self, lease), fields(version = %lease.version().0
507 ))]
508 pub fn begin_query_at_version(
509 &self,
510 lease: &VersionLeaseGuard,
511 identity: IdentityId,
512 ) -> Result<QueryTransaction> {
513 let mut txn =
514 QueryTransaction::new(self.multi.begin_query_at_version(lease)?, self.single.clone(), identity);
515 txn.set_executor(Arc::new(self.executor.clone()));
516 Ok(txn)
517 }
518
519 #[instrument(name = "engine::acquire_version_lease", level = "trace", skip(self), fields(version = %version.0))]
520 pub fn acquire_version_lease(&self, version: CommitVersion) -> Result<VersionLeaseGuard> {
521 self.multi.acquire_version_lease(version)
522 }
523
524 #[instrument(name = "engine::acquire_current_snapshot_lease", level = "trace", skip(self))]
525 pub fn acquire_current_snapshot_lease(&self) -> Result<(CommitVersion, VersionLeaseGuard)> {
526 self.multi.acquire_current_snapshot_lease()
527 }
528
529 #[inline]
530 pub fn multi(&self) -> &MultiTransaction {
531 &self.multi
532 }
533
534 #[inline]
535 pub fn multi_owned(&self) -> MultiTransaction {
536 self.multi.clone()
537 }
538
539 #[inline]
540 pub fn spawner(&self) -> ActorSpawner {
541 self.multi.spawner()
542 }
543
544 #[inline]
545 pub fn single(&self) -> &SingleTransaction {
546 &self.single
547 }
548
549 #[inline]
550 pub fn single_owned(&self) -> SingleTransaction {
551 self.single.clone()
552 }
553
554 #[inline]
555 pub fn emit<E: Event>(&self, event: E) {
556 self.event_bus.emit(event)
557 }
558
559 #[inline]
560 pub fn catalog(&self) -> Catalog {
561 self.catalog.clone()
562 }
563
564 #[inline]
565 pub fn services(&self) -> Arc<Services> {
566 self.executor.services().clone()
567 }
568
569 #[inline]
570 pub fn flow_operator_store(&self) -> &SystemFlowOperatorStore {
571 &self.flow_operator_store
572 }
573
574 #[inline]
575 pub fn current_version(&self) -> Result<CommitVersion> {
576 self.multi.current_version()
577 }
578
579 #[inline]
580 pub fn done_until(&self) -> CommitVersion {
581 self.multi.done_until()
582 }
583
584 #[inline]
585 pub fn query_done_until(&self) -> CommitVersion {
586 self.multi.query_done_until()
587 }
588
589 #[inline]
590 pub fn oracle_window_count(&self) -> usize {
591 self.multi.oracle_window_count()
592 }
593
594 #[inline]
595 pub fn wait_for_mark_timeout(&self, version: CommitVersion, timeout: Duration) -> bool {
596 self.multi.wait_for_mark_timeout(version, timeout)
597 }
598
599 #[inline]
600 pub fn notify_on_mark(&self, version: CommitVersion, callback: Box<dyn FnOnce() + Send>) {
601 self.multi.notify_on_mark(version, callback);
602 }
603
604 #[inline]
605 pub fn executor(&self) -> Executor {
606 self.executor.clone()
607 }
608
609 #[inline]
610 pub fn view_lineage(&self) -> ViewLineage {
611 self.executor.view_lineage.clone()
612 }
613
614 #[inline]
615 pub fn ioc(&self) -> &IocContainer {
616 &self.executor.ioc
617 }
618
619 #[inline]
620 pub fn cdc_store(&self) -> CdcStore {
621 self.executor.ioc.resolve::<CdcStore>().expect("CdcStore must be registered")
622 }
623
624 #[inline]
625 pub fn actor<M: 'static>(&self) -> Option<ActorRef<M>>
626 where
627 ActorRef<M>: Send + Sync,
628 {
629 self.executor.ioc.try_resolve::<ActorRef<M>>()
630 }
631
632 #[inline]
633 pub fn cdc_producer_watermark(&self) -> CommitVersion {
634 self.executor.ioc.try_resolve::<CdcProducerWatermark>().map(|w| w.get()).unwrap_or(CommitVersion(0))
635 }
636
637 #[inline]
638 pub fn cdc_consumer_watermark(&self) -> CommitVersion {
639 self.executor.ioc.try_resolve::<CdcConsumerWatermark>().map(|w| w.get()).unwrap_or(CommitVersion(0))
640 }
641
642 #[inline]
643 pub fn notify_cdc_consumers(&self) {
644 if let Some(registry) = self.executor.ioc.try_resolve::<CdcWakeRegistry>() {
645 registry.notify_all();
646 }
647 }
648
649 pub fn set_read_only(&self) {
650 self.read_only.store(true, Ordering::SeqCst);
651 }
652
653 pub fn is_read_only(&self) -> bool {
654 self.read_only.load(Ordering::SeqCst)
655 }
656
657 pub(crate) fn reject_if_read_only(&self) -> Result<()> {
658 if self.is_read_only() {
659 return Err(Error(Box::new(read_only_rejection(Fragment::None))));
660 }
661 Ok(())
662 }
663
664 pub fn set_shutting_down(&self) {
665 self.shutting_down.store(true, Ordering::SeqCst);
666 }
667
668 pub fn is_shutting_down(&self) -> bool {
669 self.shutting_down.load(Ordering::SeqCst)
670 }
671
672 pub(crate) fn reject_if_shutting_down(&self, identity: IdentityId) -> Result<()> {
673 if self.is_shutting_down() && !identity.is_system() {
674 return Err(TransactionError::ShuttingDown.into());
675 }
676 Ok(())
677 }
678
679 pub fn bulk_insert<'e>(&'e self, identity: IdentityId) -> BulkInsertBuilder<'e, Validated> {
680 BulkInsertBuilder::new(self, identity)
681 }
682
683 pub fn bulk_insert_unchecked<'e>(&'e self, identity: IdentityId) -> BulkInsertBuilder<'e, Unchecked> {
684 BulkInsertBuilder::new_unchecked(self, identity)
685 }
686}
687
688impl Shutdown for StandardEngine {
689 fn shutdown(&self) {
690 self.interceptors.clear_late();
691 self.executor.ioc.clear();
692 self.executor.virtual_table_registry.clear();
693 self.multi().store().clear_eviction_watermark();
694 #[cfg(not(reifydb_single_threaded))]
695 if let Some(registry) = self.executor.remote_registry.as_ref() {
696 registry.shutdown();
697 }
698 }
699}
700
701fn convert_vtable_user_columns_to_columns(columns: &[UserVTableColumn]) -> Vec<Column> {
702 columns.iter()
703 .enumerate()
704 .map(|(idx, col)| {
705 let constraint = TypeConstraint::unconstrained(col.data_type.clone());
706 Column {
707 id: ColumnId(idx as u64),
708 name: col.name.clone(),
709 constraint,
710 properties: vec![],
711 index: ColumnIndex(idx as u8),
712 auto_increment: false,
713 dictionary_id: None,
714 }
715 })
716 .collect()
717}