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