1pub mod cache;
10pub mod eval;
11pub mod register;
12
13use std::{
14 collections::{BTreeMap, BTreeSet},
15 sync::Arc,
16};
17
18use dashmap::DashMap;
19use reifydb_catalog::catalog::Catalog;
20#[cfg(reifydb_target = "native")]
21use reifydb_codec::value::encode_params;
22use reifydb_core::{
23 common::CommitVersion,
24 event::EventBus,
25 interface::catalog::{
26 flow::{FlowId, FlowNodeId},
27 id::{TableId, ViewId},
28 shape::ShapeId,
29 },
30};
31use reifydb_engine::vm::executor::Executor;
32#[cfg(reifydb_target = "native")]
33use reifydb_extension::operator::ffi_loader::ffi_operator_loader;
34use reifydb_rql::flow::{
35 analyzer::{FlowDependencyGraph, FlowGraphAnalyzer, FlowSchedule},
36 flow::FlowDag,
37};
38use reifydb_runtime::{
39 context::{RuntimeContext, clock::Clock},
40 sync::rwlock::{RwLock, RwLockReadGuard, RwLockWriteGuard},
41};
42#[cfg(reifydb_target = "native")]
43use reifydb_sdk::config::Config;
44use reifydb_value::value::duration::Duration;
45#[cfg(reifydb_target = "native")]
46use reifydb_value::{Result, error::Error, params::Params, value::Value};
47use tracing::instrument;
48
49#[cfg(reifydb_target = "native")]
50use crate::error::{FlowStateError, NativeOperatorError};
51#[cfg(reifydb_target = "native")]
52use crate::operator::BoxedOperator;
53#[cfg(reifydb_target = "native")]
54use crate::operator::ffi::FFIOperator;
55#[cfg(reifydb_target = "native")]
56use crate::operator::native::native_operator_loader;
57use crate::{
58 builder::CustomOperators,
59 engine::cache::{ExecutionLevelCache, ScheduleCache},
60 operator::OperatorCell,
61 transaction::allocators::FlowAllocators,
62};
63
64pub struct FlowEngineInner {
65 pub(crate) catalog: Catalog,
66 pub(crate) executor: Executor,
67 pub(crate) operators: BTreeMap<FlowNodeId, OperatorCell>,
68 pub(crate) flows: BTreeMap<FlowId, FlowDag>,
69 pub(crate) sources: BTreeMap<ShapeId, Vec<(FlowId, FlowNodeId)>>,
70 pub(crate) sinks: BTreeMap<ShapeId, Vec<(FlowId, FlowNodeId)>>,
71 pub(crate) analyzer: FlowGraphAnalyzer,
72 pub(crate) execution_level_cache: ExecutionLevelCache,
73 pub(crate) schedule_cache: ScheduleCache,
74 #[allow(dead_code)]
75 pub(crate) event_bus: EventBus,
76 pub(crate) flow_creation_versions: BTreeMap<FlowId, CommitVersion>,
77 pub(crate) runtime_context: RuntimeContext,
78 pub(crate) custom_operators: CustomOperators,
79 operator_tick_times: DashMap<FlowNodeId, u64>,
80 pub(crate) allocators: FlowAllocators,
81}
82
83#[derive(Clone)]
84pub struct FlowEngine {
85 inner: Arc<RwLock<FlowEngineInner>>,
86}
87
88impl FlowEngine {
89 pub fn new(
90 catalog: Catalog,
91 executor: Executor,
92 event_bus: EventBus,
93 runtime_context: RuntimeContext,
94 custom_operators: CustomOperators,
95 allocators: FlowAllocators,
96 ) -> Self {
97 Self {
98 inner: Arc::new(RwLock::new(FlowEngineInner::new(
99 catalog,
100 executor,
101 event_bus,
102 runtime_context,
103 custom_operators,
104 allocators,
105 ))),
106 }
107 }
108
109 pub fn read(&self) -> RwLockReadGuard<'_, FlowEngineInner> {
110 self.inner.read()
111 }
112
113 pub fn read_recursive(&self) -> RwLockReadGuard<'_, FlowEngineInner> {
114 self.inner.read_recursive()
115 }
116
117 pub fn write(&self) -> RwLockWriteGuard<'_, FlowEngineInner> {
118 self.inner.write()
119 }
120}
121
122impl FlowEngineInner {
123 #[instrument(
124 name = "flow::engine::new",
125 level = "debug",
126 skip(catalog, executor, event_bus, runtime_context, custom_operators, allocators)
127 )]
128 pub fn new(
129 catalog: Catalog,
130 executor: Executor,
131 event_bus: EventBus,
132 runtime_context: RuntimeContext,
133 custom_operators: CustomOperators,
134 allocators: FlowAllocators,
135 ) -> Self {
136 Self {
137 catalog,
138 executor,
139 operators: BTreeMap::new(),
140 flows: BTreeMap::new(),
141 sources: BTreeMap::new(),
142 sinks: BTreeMap::new(),
143 analyzer: FlowGraphAnalyzer::new(),
144 execution_level_cache: ExecutionLevelCache::new(),
145 schedule_cache: ScheduleCache::new(),
146 event_bus,
147 flow_creation_versions: BTreeMap::new(),
148 runtime_context,
149 custom_operators,
150 operator_tick_times: DashMap::new(),
151 allocators,
152 }
153 }
154
155 pub fn clock(&self) -> &Clock {
156 &self.runtime_context.clock
157 }
158
159 pub fn operator(&self, node_id: FlowNodeId) -> Option<OperatorCell> {
160 self.operators.get(&node_id).cloned()
161 }
162
163 pub fn insert_operator(&mut self, node_id: FlowNodeId, operator: OperatorCell) {
164 self.operators.insert(node_id, operator);
165 }
166
167 pub fn register_flow_dag(&mut self, flow: FlowDag) {
168 self.analyzer.add(flow.clone());
169 self.flows.insert(flow.id, flow);
170 }
171
172 pub fn flow_by_id(&self, flow_id: FlowId) -> Option<FlowDag> {
173 self.flows.get(&flow_id).cloned()
174 }
175
176 pub fn has_sources(&self) -> bool {
177 !self.sources.is_empty()
178 }
179
180 pub fn flows_for_source_shape(&self, shape: ShapeId) -> Option<Vec<(FlowId, FlowNodeId)>> {
181 self.sources.get(&shape).cloned()
182 }
183
184 pub(crate) fn operator_due(&self, node_id: FlowNodeId, now_nanos: u64, interval: Duration) -> bool {
185 let interval_nanos = interval.to_std().as_nanos() as u64;
186 let due = match self.operator_tick_times.get(&node_id) {
187 Some(last) => now_nanos.saturating_sub(*last) >= interval_nanos,
188 None => true,
189 };
190 if due {
191 self.operator_tick_times.insert(node_id, now_nanos);
192 }
193 due
194 }
195
196 #[cfg(reifydb_target = "native")]
197 #[instrument(name = "flow::engine::create_ffi_operator", level = "debug", skip(self, config), fields(operator = %operator, node_id = ?node_id))]
198 pub(crate) fn create_ffi_operator(
199 &self,
200 operator: &str,
201 node_id: FlowNodeId,
202 config: &BTreeMap<String, Value>,
203 ) -> Result<BoxedOperator> {
204 let loader = ffi_operator_loader();
205 let mut loader_write = loader.write();
206
207 let config_params =
208 Params::Named(Arc::new(config.iter().map(|(k, v)| (k.clone(), v.clone())).collect()));
209 let config_bytes = encode_params(&config_params).map_err(|e| {
210 Error::from(FlowStateError::Encode {
211 state: "operator config",
212 cause: e.to_string(),
213 })
214 })?;
215
216 let (descriptor, instance) =
217 loader_write.create_operator_by_name(operator, node_id, &config_bytes).map_err(|e| {
218 Error::from(NativeOperatorError::CreateFailed {
219 cause: format!("{:?}", e),
220 })
221 })?;
222
223 Ok(Box::new(FFIOperator::new(descriptor, instance, node_id, self.executor.clone())))
224 }
225
226 #[cfg(reifydb_target = "native")]
227 pub(crate) fn is_ffi_operator(&self, operator: &str) -> bool {
228 let loader = ffi_operator_loader();
229 let loader_read = loader.read();
230 loader_read.has_operator(operator)
231 }
232
233 #[cfg(reifydb_target = "native")]
234 #[instrument(name = "flow::engine::create_native_operator", level = "debug", skip(self, config), fields(operator = %operator, node_id = ?node_id))]
235 pub(crate) fn create_native_operator(
236 &self,
237 operator: &str,
238 node_id: FlowNodeId,
239 config: &Config,
240 ) -> Result<BoxedOperator> {
241 let loader = native_operator_loader();
242 let mut loader_write = loader.write();
243 loader_write.create_operator_by_name(operator, node_id, config)
244 }
245
246 #[cfg(reifydb_target = "native")]
247 pub(crate) fn is_native_operator(&self, operator: &str) -> bool {
248 native_operator_loader().read().has_operator(operator)
249 }
250
251 #[cfg(not(reifydb_target = "native"))]
252 #[allow(dead_code)]
253 pub(crate) fn is_ffi_operator(&self, _operator: &str) -> bool {
254 false
255 }
256
257 pub fn flow_ids(&self) -> BTreeSet<FlowId> {
258 self.flows.keys().copied().collect()
259 }
260
261 pub fn clear(&mut self) {
262 self.operators.clear();
263 self.flows.clear();
264 self.sources.clear();
265 self.sinks.clear();
266 self.analyzer.clear();
267 self.flow_creation_versions.clear();
268 self.execution_level_cache.invalidate();
269 self.schedule_cache.invalidate();
270 }
271
272 pub fn remove_flow(&mut self, flow_id: FlowId) {
273 let node_ids: Vec<FlowNodeId> =
274 self.flows.get(&flow_id).map(|flow| flow.get_node_ids().collect()).unwrap_or_default();
275
276 for node_id in node_ids {
277 self.operators.remove(&node_id);
278 self.allocators.row.evict(node_id);
279 }
280
281 for entries in self.sources.values_mut() {
282 entries.retain(|(fid, _)| *fid != flow_id);
283 }
284 self.sources.retain(|_, v| !v.is_empty());
285
286 for entries in self.sinks.values_mut() {
287 entries.retain(|(fid, _)| *fid != flow_id);
288 }
289 self.sinks.retain(|_, v| !v.is_empty());
290
291 self.flows.remove(&flow_id);
292
293 self.analyzer.remove(flow_id);
294 self.execution_level_cache.invalidate();
295 self.schedule_cache.invalidate();
296 }
297
298 pub fn get_dependency_graph(&self) -> FlowDependencyGraph {
299 self.analyzer.get_dependency_graph().clone()
300 }
301
302 pub fn get_flows_depending_on_table(&self, table_id: TableId) -> Vec<FlowId> {
303 let dependency_graph = self.analyzer.get_dependency_graph();
304 self.analyzer.get_flows_depending_on_table(dependency_graph, table_id)
305 }
306
307 pub fn get_flows_depending_on_view(&self, view_id: ViewId) -> Vec<FlowId> {
308 let dependency_graph = self.analyzer.get_dependency_graph();
309 self.analyzer.get_flows_depending_on_view(dependency_graph, view_id)
310 }
311
312 pub fn get_flow_producing_view(&self, view_id: ViewId) -> Option<FlowId> {
313 let dependency_graph = self.analyzer.get_dependency_graph();
314 self.analyzer.get_flow_producing_view(dependency_graph, view_id)
315 }
316
317 pub fn calculate_execution_levels(&self) -> Vec<Vec<FlowId>> {
318 if let Some(levels) = self.execution_level_cache.get() {
319 return levels;
320 }
321
322 let dependency_graph = self.analyzer.get_dependency_graph();
323 let levels = self.analyzer.calculate_execution_levels(dependency_graph);
324 self.execution_level_cache.set(levels.clone());
325 levels
326 }
327
328 pub fn calculate_schedule(&self) -> FlowSchedule {
329 if let Some(schedule) = self.schedule_cache.get() {
330 return schedule;
331 }
332
333 let dependency_graph = self.analyzer.get_dependency_graph();
334 let schedule = self.analyzer.calculate_schedule(dependency_graph);
335 self.schedule_cache.set(schedule.clone());
336 schedule
337 }
338}