Skip to content

Commit 19071b3

Browse files
committed
fix: minor fixes
1 parent b9b62fe commit 19071b3

8 files changed

Lines changed: 157 additions & 167 deletions

File tree

crates/taurus-core/src/ERROR_CODES.md

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
# Taurus Runtime Error Codes
22

3-
This document is the canonical catalog for runtime error codes emitted by `taurus-core`.
3+
This document is the canonical catalog for runtime error codes emitted by Taurus runtime crates (`taurus-core` and `taurus-provider`).
44

55
## Code Format
66

77
- `T-STD-XXXXX`: Errors originating inside standard function implementations under `runtime/functions/*`.
88
- `T-CORE-XXXXXX`: Errors originating from core runtime infrastructure (`engine`, `handler`, type conversion, app-layer mapping).
9+
- `T-PROV-XXXXXX`: Errors originating from provider integrations (transport adapters, remote runtime connectors).
910

1011
## Code Table
1112

@@ -29,3 +30,10 @@ This document is the canonical catalog for runtime error codes emitted by `tauru
2930
| `T-CORE-000304` | App Error Mapping | Serialization/deserialization failure mapped into runtime error format. | Encoding/decoding/parsing failure surfaced as `Error::Serialization`. | `types/errors/error.rs` |
3031
| `T-CORE-000399` | App Error Mapping | Internal application failure mapped into runtime error format. | Catch-all non-domain internal failure surfaced as `Error::Internal`. | `types/errors/error.rs` |
3132
| `T-CORE-999999` | Runtime Error Fallback | Default fallback runtime error code when no explicit mapping is provided. | `RuntimeError::default()` used as defensive fallback. | `types/errors/runtime_error.rs` |
33+
| `T-PROV-000001` | Provider Remote Runtime | Remote request to NATS did not yield a valid response message. | NATS request failed or timed out while waiting for remote runtime answer. | `taurus-provider/providers/remote/nats_remote_runtime.rs` |
34+
| `T-PROV-000002` | Provider Remote Runtime | Remote runtime response could not be decoded into expected protobuf structure. | Received payload is malformed, truncated, or schema-incompatible for `ExecutionResult`. | `taurus-provider/providers/remote/nats_remote_runtime.rs` |
35+
| `T-PROV-000003` | Provider Remote Runtime | Remote runtime response decoded, but contained no concrete result field. | `ExecutionResult` exists but `result` is `None` (protocol contract violation). | `taurus-provider/providers/remote/nats_remote_runtime.rs` |
36+
37+
## Provider Note
38+
39+
`taurus-provider` can also forward remote service errors with service-owned codes (for example codes returned inside Aquila `ExecutionResult::Error`). Those are intentionally preserved instead of remapped, so they are not enumerated as static Taurus provider codes here.

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

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,26 @@ impl ExecutionEngine {
5151
respond_emitter: Option<&dyn RespondEmitter>,
5252
with_trace: bool,
5353
) -> (Signal, ExitReason) {
54-
self.execute_graph(
54+
self.execute_flow_with_execution_id(
55+
ExecutionId::new_v4(),
56+
flow,
57+
remote,
58+
respond_emitter,
59+
with_trace,
60+
)
61+
}
62+
63+
/// Execute an `ExecutionFlow` with a caller-provided execution id.
64+
pub fn execute_flow_with_execution_id(
65+
&self,
66+
execution_id: ExecutionId,
67+
flow: ExecutionFlow,
68+
remote: Option<&dyn RemoteRuntime>,
69+
respond_emitter: Option<&dyn RespondEmitter>,
70+
with_trace: bool,
71+
) -> (Signal, ExitReason) {
72+
self.execute_graph_with_execution_id(
73+
execution_id,
5574
flow.starting_node_id,
5675
flow.node_functions,
5776
flow.input_value,
@@ -71,8 +90,28 @@ impl ExecutionEngine {
7190
respond_emitter: Option<&dyn RespondEmitter>,
7291
with_trace: bool,
7392
) -> (Signal, ExitReason) {
74-
let execution_id = ExecutionId::new_v4();
93+
self.execute_graph_with_execution_id(
94+
ExecutionId::new_v4(),
95+
start_node_id,
96+
node_functions,
97+
flow_input,
98+
remote,
99+
respond_emitter,
100+
with_trace,
101+
)
102+
}
75103

104+
/// Execute a graph described by node list and start node with a caller-provided execution id.
105+
pub fn execute_graph_with_execution_id(
106+
&self,
107+
execution_id: ExecutionId,
108+
start_node_id: i64,
109+
node_functions: Vec<NodeFunction>,
110+
flow_input: Option<Value>,
111+
remote: Option<&dyn RemoteRuntime>,
112+
respond_emitter: Option<&dyn RespondEmitter>,
113+
with_trace: bool,
114+
) -> (Signal, ExitReason) {
76115
if let Some(emitter) = respond_emitter {
77116
emitter.emit(execution_id, EmitType::StartingExec, null_value());
78117
}

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

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
//! Respond emitter abstraction used by the engine.
22
3+
use std::fmt::{Display, Formatter};
4+
35
use tucana::shared::Value;
46
use uuid::Uuid;
57

@@ -32,3 +34,16 @@ where
3234
self(execution_id, emit_type, value);
3335
}
3436
}
37+
38+
impl Display for EmitType {
39+
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
40+
let label = match self {
41+
EmitType::StartingExec => "started_execution",
42+
EmitType::OngoingExec => "ongoing_execution",
43+
EmitType::FinishedExec => "finished_execution",
44+
EmitType::FailedExec => "failed_execution",
45+
};
46+
47+
write!(f, "{label}")
48+
}
49+
}

crates/taurus-manual/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ edition.workspace = true
66
[dependencies]
77
tucana = { workspace = true }
88
taurus-core = { workspace = true }
9+
taurus-provider = { workspace = true }
910
log = { workspace = true }
1011
env_logger = { workspace = true }
1112
serde_json = { workspace = true }
@@ -15,4 +16,3 @@ tonic = { workspace = true }
1516
tokio = { workspace = true }
1617
async-nats = { workspace = true }
1718
clap = { version = "4.6.0", features= ["derive"] }
18-
async-trait = { workspace = true }

crates/taurus-manual/src/main.rs

Lines changed: 16 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,16 @@
11
use std::path::Path;
22

3-
use async_nats::Client;
4-
use async_trait::async_trait;
53
use clap::{Parser, arg, command};
6-
use log::{error, info};
7-
use prost::Message;
4+
use log::error;
5+
use log::info;
86
use serde::Deserialize;
97
use taurus_core::runtime::engine::ExecutionEngine;
10-
use taurus_core::runtime::remote::{RemoteExecution, RemoteRuntime};
11-
use taurus_core::types::errors::runtime_error::RuntimeError;
12-
use tucana::aquila::ExecutionResult;
8+
use taurus_core::types::signal::Signal;
9+
use taurus_provider::providers::emitter::nats_emitter::NATSRespondEmitter;
10+
use taurus_provider::providers::remote::nats_remote_runtime::NATSRemoteRuntime;
11+
use tucana::shared::ValidationFlow;
1312
use tucana::shared::helper::value::from_json_value;
1413
use tucana::shared::helper::value::to_json_value;
15-
use tucana::shared::{ValidationFlow, Value};
1614

1715
#[derive(Clone, Deserialize)]
1816
pub struct Input {
@@ -115,78 +113,6 @@ impl Cases {
115113
get_test_cases(path)
116114
}
117115
}
118-
pub struct RemoteNatsClient {
119-
client: Client,
120-
}
121-
122-
impl RemoteNatsClient {
123-
pub fn new(client: Client) -> Self {
124-
RemoteNatsClient { client }
125-
}
126-
}
127-
128-
#[async_trait]
129-
impl RemoteRuntime for RemoteNatsClient {
130-
async fn execute_remote(&self, execution: RemoteExecution) -> Result<Value, RuntimeError> {
131-
let topic = format!(
132-
"action.{}.{}",
133-
execution.target_service, execution.request.execution_identifier
134-
);
135-
let payload = execution.request.encode_to_vec();
136-
let res = self.client.request(topic.clone(), payload.into()).await;
137-
log::info!("Publishing to topic: {}", topic);
138-
let message = match res {
139-
Ok(r) => r,
140-
Err(err) => {
141-
log::error!(
142-
"RemoteRuntimeExeption: failed to handle NATS message: {}",
143-
err
144-
);
145-
return Err(RuntimeError::new(
146-
"T-RMT-000001",
147-
"RemoteRuntimeExeption",
148-
"Failed to receive any response messages from a remote runtime.",
149-
));
150-
}
151-
};
152-
153-
let decode_result = ExecutionResult::decode(message.payload);
154-
let execution_result = match decode_result {
155-
Ok(r) => r,
156-
Err(err) => {
157-
log::error!(
158-
"RemoteRuntimeExeption: failed to decode NATS message: {}",
159-
err
160-
);
161-
return Err(RuntimeError::new(
162-
"T-RMT-000002",
163-
"RemoteRuntimeExeption",
164-
"Failed to read Remote Response",
165-
));
166-
}
167-
};
168-
169-
match execution_result.result {
170-
Some(result) => match result {
171-
tucana::aquila::execution_result::Result::Success(value) => Ok(value),
172-
tucana::aquila::execution_result::Result::Error(err) => {
173-
let code = err.code.to_string();
174-
let description = match err.description {
175-
Some(string) => string,
176-
None => "Unknown Error".to_string(),
177-
};
178-
let error = RuntimeError::new(code, "RemoteExecutionError", description);
179-
Err(error)
180-
}
181-
},
182-
None => Err(RuntimeError::new(
183-
"T-RMT-000003",
184-
"RemoteRuntimeExeption",
185-
"Result of Remote Response was empty.",
186-
)),
187-
}
188-
}
189-
}
190116

191117
#[derive(clap::Parser, Debug)]
192118
#[command(author, version, about)]
@@ -233,35 +159,37 @@ async fn main() {
233159
panic!("Failed to connect to NATS server: {}", err);
234160
}
235161
};
236-
let remote = RemoteNatsClient::new(client);
162+
let remote = NATSRemoteRuntime::new(client.clone());
163+
let emitter = NATSRespondEmitter::new(client);
237164
let engine = ExecutionEngine::new();
238165
let (result, _) = engine.execute_graph(
239166
case.flow.starting_node_id,
240167
case.flow.node_functions.clone(),
241168
flow_input,
242169
Some(&remote),
243-
None,
244-
true,
170+
Some(&emitter),
171+
false,
245172
);
173+
emitter.shutdown().await;
246174

247175
match result {
248-
taurus_core::types::signal::Signal::Success(value) => {
176+
Signal::Success(value) => {
249177
let json = to_json_value(value);
250178
let pretty = serde_json::to_string_pretty(&json).unwrap();
251179
println!("{}", pretty);
252180
}
253-
taurus_core::types::signal::Signal::Return(value) => {
181+
Signal::Return(value) => {
254182
let json = to_json_value(value);
255183
let pretty = serde_json::to_string_pretty(&json).unwrap();
256184
println!("{}", pretty);
257185
}
258-
taurus_core::types::signal::Signal::Respond(value) => {
186+
Signal::Respond(value) => {
259187
let json = to_json_value(value);
260188
let pretty = serde_json::to_string_pretty(&json).unwrap();
261189
println!("{}", pretty);
262190
}
263-
taurus_core::types::signal::Signal::Stop => println!("Received Stop signal"),
264-
taurus_core::types::signal::Signal::Failure(runtime_error) => {
191+
Signal::Stop => println!("Received Stop signal"),
192+
Signal::Failure(runtime_error) => {
265193
println!("RuntimeError: {:?}", runtime_error);
266194
}
267195
}

crates/taurus-provider/src/providers/emitter/nats_emitter.rs

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ const DEFAULT_TOPIC_PREFIX: &str = "runtime.emitter";
1010

1111
pub struct NATSRespondEmitter {
1212
tx: mpsc::UnboundedSender<NATSEmitMessage>,
13+
worker_task: tokio::task::JoinHandle<()>,
1314
}
1415

1516
struct NATSEmitMessage {
@@ -31,8 +32,12 @@ impl NATSRespondEmitter {
3132
// This worker serializes outbound lifecycle events to one NATS topic per execution:
3233
// `<topic_prefix>.<execution_id>`.
3334
// Event type is embedded in the payload so subscribers do not need four topic bindings.
34-
tokio::spawn(async move {
35+
let worker_task = tokio::spawn(async move {
3536
while let Some(message) = rx.recv().await {
37+
log::debug!(
38+
"Emitter has been called for Emitter Signal: {}",
39+
message.emit_type
40+
);
3641
let topic = format!("{}.{}", topic_prefix, message.execution_id);
3742
let encoded_payload = encode_emit_message(message.emit_type, message.value);
3843

@@ -45,11 +50,25 @@ impl NATSRespondEmitter {
4550
topic,
4651
err
4752
);
53+
} else {
54+
log::info!(
55+
"Published runtime emit message on '{}' (type={})",
56+
topic,
57+
emit_type_as_str(message.emit_type)
58+
);
4859
}
4960
}
5061
});
5162

52-
Self { tx }
63+
Self { tx, worker_task }
64+
}
65+
66+
/// Gracefully stop the emitter worker after all queued messages are published.
67+
pub async fn shutdown(self) {
68+
drop(self.tx);
69+
if let Err(err) = self.worker_task.await {
70+
log::error!("NATS emitter worker join failed: {:?}", err);
71+
}
5372
}
5473
}
5574

crates/taurus/src/app/mod.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use code0_flow::flow_config::load_env_file;
66
use code0_flow::flow_config::mode::Mode::DYNAMIC;
77
use code0_flow::flow_service::FlowUpdateService;
88
use taurus_core::runtime::engine::ExecutionEngine;
9+
use taurus_provider::providers::emitter::nats_emitter::NATSRespondEmitter;
910
use taurus_provider::providers::remote::nats_remote_runtime::NATSRemoteRuntime;
1011
use tokio::signal;
1112
use tokio::task::JoinHandle;
@@ -30,7 +31,14 @@ pub async fn run() {
3031
setup_dynamic_services_if_needed(&config).await;
3132

3233
let nats_remote = NATSRemoteRuntime::new(client.clone());
33-
let mut worker_task = worker::spawn_worker(client, engine, nats_remote, runtime_usage_service);
34+
let runtime_emitter = NATSRespondEmitter::new(client.clone());
35+
let mut worker_task = worker::spawn_worker(
36+
client,
37+
engine,
38+
nats_remote,
39+
runtime_emitter,
40+
runtime_usage_service,
41+
);
3442

3543
wait_for_shutdown(&mut worker_task, &mut health_task).await;
3644
update_stopped_status(runtime_status_service.as_ref()).await;

0 commit comments

Comments
 (0)