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