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