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