Skip to content

Commit 45cc1d1

Browse files
committed
feat: refactored internal store to use node execution results instead of node results
1 parent 346d33a commit 45cc1d1

9 files changed

Lines changed: 229 additions & 99 deletions

File tree

crates/taurus-core/src/runtime/engine/compiler.rs

Lines changed: 53 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
33
use std::collections::HashMap;
44

5-
use tucana::shared::{NodeFunction, node_value};
5+
use tucana::shared::{NodeFunction, node_value, sub_flow};
66

77
use crate::{
88
runtime::engine::model::{
@@ -27,6 +27,15 @@ pub enum CompileError {
2727
node_id: i64,
2828
parameter_index: usize,
2929
},
30+
SubFlowExecutionReferenceMissing {
31+
node_id: i64,
32+
parameter_index: usize,
33+
},
34+
SubFlowFunctionIdentifierUnsupported {
35+
node_id: i64,
36+
parameter_index: usize,
37+
function_identifier: String,
38+
},
3039
}
3140

3241
impl CompileError {
@@ -64,6 +73,29 @@ impl CompileError {
6473
node_id, parameter_index
6574
),
6675
),
76+
CompileError::SubFlowExecutionReferenceMissing {
77+
node_id,
78+
parameter_index,
79+
} => RuntimeError::new(
80+
"T-CORE-000105",
81+
"FlowCompileError",
82+
format!(
83+
"Node {} parameter {} sub_flow is missing execution reference",
84+
node_id, parameter_index
85+
),
86+
),
87+
CompileError::SubFlowFunctionIdentifierUnsupported {
88+
node_id,
89+
parameter_index,
90+
function_identifier,
91+
} => RuntimeError::new(
92+
"T-CORE-000106",
93+
"FlowCompileError",
94+
format!(
95+
"Node {} parameter {} uses unsupported sub_flow function identifier {}",
96+
node_id, parameter_index, function_identifier
97+
),
98+
),
6799
}
68100
}
69101
}
@@ -125,7 +157,26 @@ pub fn compile_flow(
125157
let arg = match value {
126158
node_value::Value::LiteralValue(v) => CompiledArg::Literal(v.clone()),
127159
node_value::Value::ReferenceValue(r) => CompiledArg::Reference(r.clone()),
128-
node_value::Value::SubFlow(_sub_flow) => unimplemented!("Taurus needs to handle SubFlows (issue nr #184)"),
160+
node_value::Value::SubFlow(sub_flow) => {
161+
match sub_flow.execution_reference.as_ref() {
162+
Some(sub_flow::ExecutionReference::StartingNodeId(node_id)) => {
163+
CompiledArg::DeferredNode(*node_id)
164+
}
165+
Some(sub_flow::ExecutionReference::FunctionIdentifier(identifier)) => {
166+
return Err(CompileError::SubFlowFunctionIdentifierUnsupported {
167+
node_id: node.database_id,
168+
parameter_index,
169+
function_identifier: identifier.clone(),
170+
});
171+
}
172+
None => {
173+
return Err(CompileError::SubFlowExecutionReferenceMissing {
174+
node_id: node.database_id,
175+
parameter_index,
176+
});
177+
}
178+
}
179+
}
129180
};
130181

131182
parameters.push(CompiledParameter {

crates/taurus-core/src/runtime/engine/executor.rs

Lines changed: 47 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,10 @@ use std::collections::HashMap;
55

66
use futures_lite::future::block_on;
77
use tucana::aquila::ActionExecutionRequest;
8+
use tucana::shared::node_execution_result::Result as TucanaNodeResult;
89
use tucana::shared::reference_value::Target;
910
use tucana::shared::value::Kind;
10-
use tucana::shared::{Struct, Value};
11+
use tucana::shared::{NodeExecutionResult as TucanaNodeExecutionResult, Struct, Value};
1112
use uuid::Uuid;
1213

1314
use crate::handler::argument::{Argument, ParameterNode};
@@ -56,7 +57,7 @@ struct ExecutionResult {
5657
}
5758

5859
/// Result of executing exactly one compiled node.
59-
struct NodeExecutionResult {
60+
struct NodeResult {
6061
signal: Signal,
6162
frame_id: Option<u64>,
6263
}
@@ -150,25 +151,24 @@ impl<'a> EngineExecutor<'a> {
150151
}
151152
}
152153

153-
fn execute_single_node(
154-
&self,
155-
node_idx: usize,
156-
value_store: &mut ValueStore,
157-
) -> NodeExecutionResult {
154+
fn execute_single_node(&self, node_idx: usize, value_store: &mut ValueStore) -> NodeResult {
158155
let node = &self.flow.nodes[node_idx];
159156
// InputType references resolve against the currently running node.
160157
value_store.set_current_node_id(node.id);
161158

162159
let frame_id = self.trace_enter(node, value_store);
163160
let signal = match &node.execution_target {
164-
NodeExecutionTarget::Local => self.execute_local_node(node, value_store, frame_id),
161+
NodeExecutionTarget::Local => {
162+
let signal = self.execute_local_node(node, value_store, frame_id);
163+
self.commit_result(node.id, signal, value_store)
164+
}
165165
NodeExecutionTarget::Remote { service } => {
166166
self.execute_remote_node(node, service, value_store, frame_id)
167167
}
168168
};
169169
self.trace_exit(frame_id, &signal, value_store);
170170

171-
NodeExecutionResult { signal, frame_id }
171+
NodeResult { signal, frame_id }
172172
}
173173

174174
fn execute_local_node(
@@ -190,14 +190,11 @@ impl<'a> EngineExecutor<'a> {
190190

191191
let mut args = match self.build_args(node, value_store, frame_id) {
192192
Ok(args) => args,
193-
Err(err) => {
194-
value_store.insert_error(node.id, err.clone());
195-
return Signal::Failure(err);
196-
}
193+
Err(err) => return Signal::Failure(err),
197194
};
198195

199196
if let Some(signal) = self.force_eager_args(entry, &mut args, value_store, frame_id) {
200-
return self.commit_result(node.id, signal, value_store);
197+
return signal;
201198
}
202199

203200
// Handler-owned runtime calls (for lazy args / callbacks) re-enter the same executor.
@@ -211,8 +208,7 @@ impl<'a> EngineExecutor<'a> {
211208
child_result.signal
212209
};
213210

214-
let signal = (entry.handler)(&args, value_store, &mut run);
215-
self.commit_result(node.id, signal, value_store)
211+
(entry.handler)(&args, value_store, &mut run)
216212
}
217213

218214
fn execute_remote_node(
@@ -225,20 +221,21 @@ impl<'a> EngineExecutor<'a> {
225221
let remote_runtime = match self.remote {
226222
Some(remote) => remote,
227223
None => {
228-
return Signal::Failure(RuntimeError::new(
229-
"T-CORE-000003",
230-
"RemoteRuntimeNotConfigured",
231-
"Remote runtime not configured",
232-
));
224+
return self.commit_result(
225+
node.id,
226+
Signal::Failure(RuntimeError::new(
227+
"T-CORE-000003",
228+
"RemoteRuntimeNotConfigured",
229+
"Remote runtime not configured",
230+
)),
231+
value_store,
232+
);
233233
}
234234
};
235235

236236
let mut args = match self.build_args(node, value_store, frame_id) {
237237
Ok(args) => args,
238-
Err(err) => {
239-
value_store.insert_error(node.id, err.clone());
240-
return Signal::Failure(err);
241-
}
238+
Err(err) => return self.commit_result(node.id, Signal::Failure(err), value_store),
242239
};
243240

244241
let values = match self.resolve_remote_args(&mut args, value_store, frame_id) {
@@ -248,21 +245,16 @@ impl<'a> EngineExecutor<'a> {
248245

249246
let request = match self.build_remote_request(node, values) {
250247
Ok(request) => request,
251-
Err(err) => {
252-
value_store.insert_error(node.id, err.clone());
253-
return Signal::Failure(err);
254-
}
248+
Err(err) => return self.commit_result(node.id, Signal::Failure(err), value_store),
255249
};
256250

257-
let signal = match block_on(remote_runtime.execute_remote(RemoteExecution {
251+
match block_on(remote_runtime.execute_remote(RemoteExecution {
258252
target_service: service.to_string(),
259253
request,
260254
})) {
261-
Ok(value) => Signal::Success(value),
262-
Err(err) => Signal::Failure(err),
263-
};
264-
265-
self.commit_result(node.id, signal, value_store)
255+
Ok(result) => self.commit_remote_result(node.id, result, value_store),
256+
Err(err) => self.commit_result(node.id, Signal::Failure(err), value_store),
257+
}
266258
}
267259

268260
fn build_args(
@@ -522,6 +514,26 @@ impl<'a> EngineExecutor<'a> {
522514
}
523515
}
524516

517+
fn commit_remote_result(
518+
&self,
519+
node_id: i64,
520+
result: TucanaNodeExecutionResult,
521+
value_store: &mut ValueStore,
522+
) -> Signal {
523+
value_store.insert_node_result(node_id, result.clone());
524+
match result.result {
525+
Some(TucanaNodeResult::Success(value)) => Signal::Success(value),
526+
Some(TucanaNodeResult::Error(error)) => {
527+
Signal::Failure(RuntimeError::from_tucana_error(&error))
528+
}
529+
None => Signal::Failure(RuntimeError::new(
530+
"T-CORE-000006",
531+
"NodeExecutionResultMissingOutcome",
532+
"Remote node execution result is missing success/error outcome",
533+
)),
534+
}
535+
}
536+
525537
fn trace_enter(&self, node: &CompiledNode, value_store: &ValueStore) -> Option<u64> {
526538
self.tracer.map(|tracer| {
527539
tracer.borrow_mut().enter_node(

crates/taurus-core/src/runtime/execution/render.rs

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@
22
33
use std::collections::HashMap;
44

5+
use tucana::shared::node_execution_result::Result as TucanaNodeResult;
6+
57
use crate::runtime::execution::trace::{
6-
ArgKind, EdgeKind, Outcome, StoreDiff, StoreResultStatus, TraceFrame, TraceRun,
8+
ArgKind, EdgeKind, Outcome, StoreDiff, TraceFrame, TraceRun,
79
};
810

911
struct TraceTheme;
@@ -266,14 +268,17 @@ fn render_store_diff(
266268
}
267269

268270
for set in &diff.result_sets {
269-
let status = match set.status {
270-
StoreResultStatus::Success => "success",
271-
StoreResultStatus::Error => "error",
271+
let status = match &set.result.result {
272+
Some(TucanaNodeResult::Success(_)) => "success",
273+
Some(TucanaNodeResult::Error(_)) => "error",
274+
None => "empty",
272275
};
273276
out.push_str(&format!(
274-
"{step:04} {prefix}{continuation}{store:<5} result.set node={} [{}] {}\n",
277+
"{step:04} {prefix}{continuation}{store:<5} result.set node={} [{}] started_at={} finished_at={} {}\n",
275278
set.node_id,
276279
status,
280+
set.result.started_at,
281+
set.result.finished_at,
277282
set.preview,
278283
step = *step,
279284
prefix = prefix,

crates/taurus-core/src/runtime/execution/store.rs

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,10 @@
22
33
use std::collections::HashMap;
44

5-
use tucana::shared::Value;
5+
use tucana::shared::{NodeExecutionResult, Value};
66

7-
use crate::types::errors::runtime_error::RuntimeError;
87
use crate::types::execution::ids::{FrameId, NodeId};
98

10-
/// Runtime outcome persisted per node.
11-
#[derive(Debug, Clone)]
12-
pub enum NodeOutcome {
13-
Success(Value),
14-
Failure(RuntimeError),
15-
}
16-
179
/// Input slot key for runtime-provided temporary inputs (for iterators/predicates).
1810
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1911
pub struct InputSlotKey {
@@ -25,7 +17,7 @@ pub struct InputSlotKey {
2517
/// Store that captures mutable runtime execution state.
2618
#[derive(Debug, Clone, Default)]
2719
pub struct ExecutionStore {
28-
pub node_outcomes: HashMap<NodeId, NodeOutcome>,
20+
pub node_results: HashMap<NodeId, NodeExecutionResult>,
2921
pub input_slots: HashMap<InputSlotKey, Value>,
3022
pub flow_input: Option<Value>,
3123
pub current_node: Option<NodeId>,

crates/taurus-core/src/runtime/execution/trace.rs

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
use std::collections::HashMap;
1010
use std::time::Instant;
1111

12+
use tucana::shared::NodeExecutionResult;
13+
1214
/// Relationship between two execution frames.
1315
#[derive(Debug, Clone)]
1416
pub enum EdgeKind {
@@ -69,20 +71,13 @@ pub enum Outcome {
6971
}
7072

7173
/// One stored node result entry at snapshot time.
72-
#[derive(Debug, Clone, PartialEq, Eq)]
74+
#[derive(Debug, Clone, PartialEq)]
7375
pub struct StoreResultEntry {
7476
pub node_id: i64,
75-
pub status: StoreResultStatus,
77+
pub result: NodeExecutionResult,
7678
pub preview: String,
7779
}
7880

79-
/// Result status in the value store.
80-
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
81-
pub enum StoreResultStatus {
82-
Success,
83-
Error,
84-
}
85-
8681
/// One temporary input slot entry at snapshot time.
8782
#[derive(Debug, Clone, PartialEq, Eq)]
8883
pub struct StoreInputSlotEntry {
@@ -93,7 +88,7 @@ pub struct StoreInputSlotEntry {
9388
}
9489

9590
/// Value store snapshot attached to a frame boundary.
96-
#[derive(Debug, Clone, PartialEq, Eq)]
91+
#[derive(Debug, Clone, PartialEq)]
9792
pub struct StoreSnapshot {
9893
pub current_node_id: i64,
9994
pub flow_input_preview: String,
@@ -102,7 +97,7 @@ pub struct StoreSnapshot {
10297
}
10398

10499
/// Per-frame store changes between `store_before` and `store_after`.
105-
#[derive(Debug, Clone, Default, PartialEq, Eq)]
100+
#[derive(Debug, Clone, Default, PartialEq)]
106101
pub struct StoreDiff {
107102
pub current_node_changed: Option<(i64, i64)>,
108103
pub result_sets: Vec<StoreResultEntry>,
@@ -129,7 +124,7 @@ impl StoreDiff {
129124
for (node_id, after_entry) in &after_results {
130125
match before_results.get(node_id) {
131126
Some(before_entry)
132-
if before_entry.status == after_entry.status
127+
if before_entry.result == after_entry.result
133128
&& before_entry.preview == after_entry.preview => {}
134129
_ => result_sets.push((*after_entry).clone()),
135130
}

0 commit comments

Comments
 (0)