From 59db71d47416375cfa13c9955e58140f1085cd31 Mon Sep 17 00:00:00 2001 From: Raphael Date: Sat, 6 Jun 2026 21:09:42 +0200 Subject: [PATCH 1/2] fix: handled duration for nodes --- crates/taurus-core/src/runtime/engine.rs | 110 ++++++++++++++++++ .../src/runtime/engine/executor.rs | 62 ++++++++-- .../src/runtime/execution/value_store.rs | 55 ++++++--- crates/taurus-core/src/time.rs | 7 ++ crates/taurus-manual/src/main.rs | 95 ++++++++++++++- crates/taurus/src/app/worker.rs | 49 ++++++-- 6 files changed, 342 insertions(+), 36 deletions(-) diff --git a/crates/taurus-core/src/runtime/engine.rs b/crates/taurus-core/src/runtime/engine.rs index 529a75b..08fdb54 100644 --- a/crates/taurus-core/src/runtime/engine.rs +++ b/crates/taurus-core/src/runtime/engine.rs @@ -265,8 +265,12 @@ impl ExecutionEngine { #[cfg(test)] mod tests { use super::*; + use crate::handler::argument::Argument; + use crate::handler::registry::{FunctionRegistration, FunctionStore, ThunkRunner}; + use crate::runtime::execution::value_store::ValueStore; use crate::types::exit_reason::ExitReason; use std::cell::RefCell; + use std::time::Duration; use tucana::shared::{ InputType, ListValue, NodeParameter, NodeValue, ReferenceValue, Struct, SubFlow, SubFlowSetting, Value, node_execution_result, node_value, reference_value, @@ -405,6 +409,15 @@ mod tests { } } + fn sleep_handler( + _args: &[Argument], + _ctx: &mut ValueStore, + _run: &mut ThunkRunner<'_>, + ) -> Signal { + std::thread::sleep(Duration::from_millis(2)); + Signal::Success(null_value()) + } + fn input_type_ref_param( database_id: i64, runtime_parameter_id: &str, @@ -925,6 +938,103 @@ mod tests { )); } + #[test] + fn node_execution_result_tracks_actual_node_duration() { + let mut handlers = FunctionStore::new(); + handlers.populate(&[FunctionRegistration::eager("test::sleep", sleep_handler, 0)]); + let engine = ExecutionEngine { handlers }; + let sleep_node = node(1, "test::sleep", vec![], None); + + let report = engine.execute_graph_report(1, vec![sleep_node], None, None, None, false); + + assert_eq!(report.exit_reason, ExitReason::Success); + assert_eq!(report.node_execution_results.len(), 1); + + let node_result = &report.node_execution_results[0]; + assert_eq!(node_result.node_id, 1); + assert!(node_result.started_at >= 1_000_000_000_000_000); + assert!(node_result.finished_at > node_result.started_at); + assert!(node_result.finished_at - node_result.started_at >= 1_000); + } + + #[test] + fn execution_report_keeps_every_for_each_callback_execution() { + let engine = ExecutionEngine::new(); + let for_each_node = node( + 1, + "std::list::for_each", + vec![ + literal_param( + 100, + "list", + list_value(vec![int_value(1), int_value(2), int_value(3)]), + ), + thunk_param(101, "consumer", 2), + ], + None, + ); + let callback_node = node( + 2, + "std::number::add", + vec![ + input_type_ref_param(200, "first", 1, 1, 0), + literal_param(201, "second", int_value(2)), + ], + None, + ); + + let report = engine.execute_graph_report( + 1, + vec![for_each_node, callback_node], + None, + None, + None, + false, + ); + + assert_eq!(report.exit_reason, ExitReason::Success); + assert_eq!(report.node_execution_results.len(), 4); + + let callback_results: Vec<_> = report + .node_execution_results + .iter() + .filter(|result| result.node_id == 2) + .collect(); + assert_eq!(callback_results.len(), 3); + + let callback_values: Vec<_> = callback_results + .iter() + .map(|result| match result.result.as_ref() { + Some(node_execution_result::Result::Success(value)) => value.clone(), + other => panic!("expected callback success result, got {:?}", other), + }) + .collect(); + + assert_eq!( + callback_values, + vec![int_value(3), int_value(4), int_value(5)] + ); + let callback_parameters: Vec<_> = callback_results + .iter() + .map(|result| { + result + .parameter_results + .iter() + .map(|parameter| parameter.value.clone()) + .collect::>() + }) + .collect(); + assert_eq!( + callback_parameters, + vec![ + vec![Some(int_value(1)), Some(int_value(2))], + vec![Some(int_value(2)), Some(int_value(2))], + vec![Some(int_value(3)), Some(int_value(2))], + ] + ); + assert_eq!(report.node_execution_results[3].node_id, 1); + } + #[test] fn emitter_emits_start_and_finish_for_successful_execution() { let engine = ExecutionEngine::new(); diff --git a/crates/taurus-core/src/runtime/engine/executor.rs b/crates/taurus-core/src/runtime/engine/executor.rs index 6e36680..48254a1 100644 --- a/crates/taurus-core/src/runtime/engine/executor.rs +++ b/crates/taurus-core/src/runtime/engine/executor.rs @@ -26,6 +26,7 @@ use crate::runtime::execution::trace::{ use crate::runtime::execution::tracer::{ExecutionTracer, Tracer}; use crate::runtime::execution::value_store::{ValueStore, ValueStoreResult}; use crate::runtime::remote::{RemoteExecution, RemoteRuntime}; +use crate::time::now_unix_micros; use crate::types::errors::runtime_error::RuntimeError; use crate::types::signal::Signal; @@ -66,6 +67,8 @@ struct NodeResult { signal: Signal, frame_id: Option, parameter_results: Vec, + started_at: i64, + finished_at: i64, } struct ExecutedNode { @@ -125,10 +128,12 @@ impl<'a> EngineExecutor<'a> { emitter.emit(self.execution_id, EmitType::OngoingExec, value.clone()); } - value_store.insert_success_with_parameters( + value_store.insert_success_with_timing( node_id, value.clone(), result.parameter_results, + result.started_at, + result.finished_at, ); match next_idx { Some(next) => current_idx = next, @@ -243,25 +248,38 @@ impl<'a> EngineExecutor<'a> { let frame_id = self.trace_enter(node, value_store); let result = match &node.execution_target { NodeExecutionTarget::Local => { + let started_at = now_unix_micros(); let executed = self.execute_local_node(node, value_store, frame_id); + let finished_at = now_unix_micros(); let parameter_results = executed.parameter_results; let signal = self.commit_result( node.id, executed.signal, parameter_results.clone(), + started_at, + finished_at, value_store, ); NodeResult { signal, frame_id, parameter_results, + started_at, + finished_at, + } + } + NodeExecutionTarget::Remote { service } => { + let started_at = now_unix_micros(); + let signal = self.execute_remote_node(node, service, value_store, frame_id); + let finished_at = now_unix_micros(); + NodeResult { + signal, + frame_id, + parameter_results: Vec::new(), + started_at, + finished_at, } } - NodeExecutionTarget::Remote { service } => NodeResult { - signal: self.execute_remote_node(node, service, value_store, frame_id), - frame_id, - parameter_results: Vec::new(), - }, }; self.trace_exit(frame_id, &result.signal, value_store); @@ -331,6 +349,7 @@ impl<'a> EngineExecutor<'a> { value_store: &mut ValueStore, frame_id: Option, ) -> Signal { + let started_at = now_unix_micros(); let remote_runtime = match self.remote { Some(remote) => remote, None => { @@ -342,6 +361,8 @@ impl<'a> EngineExecutor<'a> { "Remote runtime not configured", )), Vec::new(), + started_at, + now_unix_micros(), value_store, ); } @@ -350,7 +371,14 @@ impl<'a> EngineExecutor<'a> { let mut args = match self.build_args(node, value_store, frame_id) { Ok(args) => args, Err(err) => { - return self.commit_result(node.id, Signal::Failure(err), Vec::new(), value_store); + return self.commit_result( + node.id, + Signal::Failure(err), + Vec::new(), + started_at, + now_unix_micros(), + value_store, + ); } }; @@ -361,6 +389,8 @@ impl<'a> EngineExecutor<'a> { node.id, signal, parameter_results_from_args(&args), + started_at, + now_unix_micros(), value_store, ); } @@ -374,6 +404,8 @@ impl<'a> EngineExecutor<'a> { node.id, Signal::Failure(err), parameter_results, + started_at, + now_unix_micros(), value_store, ); } @@ -390,6 +422,8 @@ impl<'a> EngineExecutor<'a> { node.id, Signal::Failure(err), parameter_results, + started_at, + now_unix_micros(), value_store, ), } @@ -678,19 +712,29 @@ impl<'a> EngineExecutor<'a> { node_id: i64, signal: Signal, parameter_results: Vec, + started_at: i64, + finished_at: i64, value_store: &mut ValueStore, ) -> Signal { match signal { Signal::Success(value) => { - value_store.insert_success_with_parameters( + value_store.insert_success_with_timing( node_id, value.clone(), parameter_results, + started_at, + finished_at, ); Signal::Success(value) } Signal::Failure(err) => { - value_store.insert_error_with_parameters(node_id, err.clone(), parameter_results); + value_store.insert_error_with_timing( + node_id, + err.clone(), + parameter_results, + started_at, + finished_at, + ); Signal::Failure(err) } // Control signals are transient and should not be cached as node outputs. diff --git a/crates/taurus-core/src/runtime/execution/value_store.rs b/crates/taurus-core/src/runtime/execution/value_store.rs index 348de26..c36f3b0 100644 --- a/crates/taurus-core/src/runtime/execution/value_store.rs +++ b/crates/taurus-core/src/runtime/execution/value_store.rs @@ -9,7 +9,7 @@ use tucana::shared::{ }; use crate::runtime::execution::trace::{StoreInputSlotEntry, StoreResultEntry, StoreSnapshot}; -use crate::time::now_unix_ms; +use crate::time::now_unix_micros; use crate::types::errors::runtime_error::RuntimeError; #[derive(Clone)] @@ -21,7 +21,8 @@ pub enum ValueStoreResult { #[derive(Default)] pub struct ValueStore { - results: HashMap, + latest_results: HashMap, + result_history: Vec, input_types: HashMap, flow_input: Value, current_node_id: i64, @@ -31,7 +32,8 @@ pub struct ValueStore { impl ValueStore { pub fn new(flow_input: Value) -> Self { Self { - results: HashMap::new(), + latest_results: HashMap::new(), + result_history: Vec::new(), input_types: HashMap::new(), flow_input, current_node_id: 0, @@ -103,7 +105,7 @@ impl ValueStore { } fn get_result(&mut self, id: i64) -> ValueStoreResult { - match self.results.get(&id) { + match self.latest_results.get(&id) { Some(result) => match &result.result { Some(TucanaNodeResult::Success(value)) => ValueStoreResult::Success(value.clone()), Some(TucanaNodeResult::Error(error)) => { @@ -155,13 +157,24 @@ impl ValueStore { value: Value, parameter_results: Vec, ) { - let ts = now_unix_ms(); + let ts = now_unix_micros(); + self.insert_success_with_timing(id, value, parameter_results, ts, ts); + } + + pub fn insert_success_with_timing( + &mut self, + id: i64, + value: Value, + parameter_results: Vec, + started_at: i64, + finished_at: i64, + ) { self.insert_node_result( id, NodeExecutionResult { node_id: id, - started_at: ts, - finished_at: ts, + started_at, + finished_at, parameter_results, result: Some(TucanaNodeResult::Success(value)), }, @@ -178,13 +191,24 @@ impl ValueStore { runtime_error: RuntimeError, parameter_results: Vec, ) { - let ts = now_unix_ms(); + let ts = now_unix_micros(); + self.insert_error_with_timing(id, runtime_error, parameter_results, ts, ts); + } + + pub fn insert_error_with_timing( + &mut self, + id: i64, + runtime_error: RuntimeError, + parameter_results: Vec, + started_at: i64, + finished_at: i64, + ) { self.insert_node_result( id, NodeExecutionResult { node_id: id, - started_at: ts, - finished_at: ts, + started_at, + finished_at, parameter_results, result: Some(TucanaNodeResult::Error(runtime_error.as_tucana_error())), }, @@ -193,13 +217,12 @@ impl ValueStore { pub fn insert_node_result(&mut self, id: i64, mut result: NodeExecutionResult) { result.node_id = id; - self.results.insert(id, result); + self.latest_results.insert(id, result.clone()); + self.result_history.push(result); } pub fn node_execution_results(&self) -> Vec { - let mut results: Vec<_> = self.results.values().cloned().collect(); - results.sort_by_key(|result| result.node_id); - results + self.result_history.clone() } pub fn push_runtime_trace_label(&mut self, label: String) { @@ -211,8 +234,8 @@ impl ValueStore { } pub fn trace_snapshot(&self) -> StoreSnapshot { - let mut results = Vec::with_capacity(self.results.len()); - for (node_id, result) in &self.results { + let mut results = Vec::with_capacity(self.latest_results.len()); + for (node_id, result) in &self.latest_results { let preview = match &result.result { Some(TucanaNodeResult::Success(value)) => preview_value(value), Some(TucanaNodeResult::Error(err)) => { diff --git a/crates/taurus-core/src/time.rs b/crates/taurus-core/src/time.rs index e3a90e4..8aab1bd 100644 --- a/crates/taurus-core/src/time.rs +++ b/crates/taurus-core/src/time.rs @@ -2,6 +2,13 @@ use std::time::{SystemTime, UNIX_EPOCH}; +pub fn now_unix_micros() -> i64 { + SystemTime::now() + .duration_since(UNIX_EPOCH) + .map(|it| it.as_micros() as i64) + .unwrap_or(0) +} + pub fn now_unix_ms() -> i64 { SystemTime::now() .duration_since(UNIX_EPOCH) diff --git a/crates/taurus-manual/src/main.rs b/crates/taurus-manual/src/main.rs index 4087aa2..627151d 100644 --- a/crates/taurus-manual/src/main.rs +++ b/crates/taurus-manual/src/main.rs @@ -1,4 +1,5 @@ use std::path::Path; +use std::time::Instant; use clap::Parser; use log::error; @@ -6,10 +7,12 @@ use log::info; use prost::Message; use serde::Deserialize; use taurus_core::runtime::engine::{ExecutionEngine, ExecutionId}; +use taurus_core::time::now_unix_micros; use taurus_core::types::signal::Signal; use taurus_provider::providers::emitter::nats_emitter::NATSRespondEmitter; use taurus_provider::providers::remote::nats_remote_runtime::NATSRemoteRuntime; use tucana::shared::ExecutionFlow; +use tucana::shared::NodeExecutionResult; use tucana::shared::ValidationFlow; use tucana::shared::helper::value::from_json_value; use tucana::shared::helper::value::to_json_value; @@ -134,6 +137,10 @@ struct Args { /// Queue the selected flow on a running Taurus instance instead of executing locally #[arg(long, default_value_t = false)] queue_execution: bool, + + /// Execute locally without connecting to NATS remote runtime or emitter + #[arg(long, default_value_t = false)] + offline: bool, } #[tokio::main] @@ -148,6 +155,10 @@ async fn main() { let path = args.path; let case = Case::from_path(&path); + if args.offline && args.queue_execution { + panic!("--offline cannot be combined with --queue-execution"); + } + let flow_input = match case.inputs.get(index as usize) { Some(inp) => match inp.input.clone() { Some(json_input) => Some(from_json_value(json_input)), @@ -156,6 +167,30 @@ async fn main() { None => None, }; + if args.offline { + let engine = ExecutionEngine::new(); + let started_at = now_unix_micros(); + let start = Instant::now(); + let report = engine.execute_graph_report( + case.flow.starting_node_id, + case.flow.node_functions.clone(), + flow_input, + None, + None, + true, + ); + let duration_us = start.elapsed().as_micros(); + let finished_at = now_unix_micros(); + print_timing_debug( + started_at, + finished_at, + duration_us, + &report.node_execution_results, + ); + print_signal(report.signal); + return; + } + let client = match async_nats::connect(nats_url).await { Ok(client) => { log::info!("Connected to nats server"); @@ -174,7 +209,10 @@ async fn main() { let remote = NATSRemoteRuntime::new(client.clone()); let emitter = NATSRespondEmitter::new(client); let engine = ExecutionEngine::new(); - let (result, _) = engine.execute_graph( + + let started_at = now_unix_micros(); + let start = Instant::now(); + let report = engine.execute_graph_report( case.flow.starting_node_id, case.flow.node_functions.clone(), flow_input, @@ -182,9 +220,21 @@ async fn main() { Some(&emitter), false, ); + let duration_us = start.elapsed().as_micros(); + let finished_at = now_unix_micros(); + print_timing_debug( + started_at, + finished_at, + duration_us, + &report.node_execution_results, + ); emitter.shutdown().await; - match result { + print_signal(report.signal); +} + +fn print_signal(signal: Signal) { + match signal { Signal::Success(value) => { let json = to_json_value(value); let pretty = serde_json::to_string_pretty(&json).unwrap(); @@ -249,3 +299,44 @@ async fn queue_execution( println!("{}", execution_id); } + +fn print_timing_debug( + started_at: i64, + finished_at: i64, + duration_us: u128, + node_results: &[NodeExecutionResult], +) { + eprintln!("[manual timing] unit=microseconds"); + eprintln!("[manual timing] started_at_unix_us={}", started_at); + eprintln!("[manual timing] finished_at_unix_us={}", finished_at); + eprintln!("[manual timing] wall_delta_us={}", finished_at - started_at); + eprintln!("[manual timing] instant_duration_us={}", duration_us); + eprintln!("[manual timing] node_count={}", node_results.len()); + + for (execution_index, result) in node_results.iter().enumerate() { + let params = result + .parameter_results + .iter() + .enumerate() + .map(|(param_index, param)| { + let value = param + .value + .clone() + .map(to_json_value) + .unwrap_or(serde_json::Value::Null); + format!("{}={}", param_index, value) + }) + .collect::>() + .join(", "); + + eprintln!( + "[manual timing] execution_index={} node_id={} started_at_unix_us={} finished_at_unix_us={} delta_us={} params=[{}]", + execution_index, + result.node_id, + result.started_at, + result.finished_at, + result.finished_at - result.started_at, + params + ); + } +} diff --git a/crates/taurus/src/app/worker.rs b/crates/taurus/src/app/worker.rs index fef777d..d08ce13 100644 --- a/crates/taurus/src/app/worker.rs +++ b/crates/taurus/src/app/worker.rs @@ -3,7 +3,8 @@ use std::time::Instant; use futures_lite::StreamExt; use prost::Message; use taurus_core::runtime::engine::{EmitType, ExecutionEngine, ExecutionId, RespondEmitter}; -use taurus_core::time::now_unix_ms; +use taurus_core::runtime::remote::RemoteRuntime; +use taurus_core::time::now_unix_micros; use taurus_core::types::errors::runtime_error::RuntimeError; use taurus_core::types::signal::Signal; use taurus_provider::providers::emitter::nats_emitter::NATSRespondEmitter; @@ -113,7 +114,7 @@ async fn process_execution_message( requested_execution_id, flow, engine, - nats_remote, + Some(nats_remote), Some(&respond_emitter), ); log::debug!( @@ -160,22 +161,22 @@ fn execute_flow( execution_id: ExecutionId, flow: ExecutionFlow, engine: &ExecutionEngine, - nats_remote: &NATSRemoteRuntime, + remote: Option<&dyn RemoteRuntime>, respond_emitter: Option<&dyn RespondEmitter>, ) -> FlowRunResult { - let started_at = now_unix_ms(); + let started_at = now_unix_micros(); let start = Instant::now(); let flow_id = flow.flow_id; let input = flow.input_value.clone(); let report = engine.execute_flow_with_execution_id_report( execution_id, flow, - Some(nats_remote), + remote, respond_emitter, true, ); - let finished_at = now_unix_ms(); - let duration_millis = start.elapsed().as_millis() as i64; + let finished_at = now_unix_micros(); + let duration_micros = start.elapsed().as_micros() as i64; FlowRunResult { execution_id, @@ -187,7 +188,7 @@ fn execute_flow( node_execution_results: report.node_execution_results, runtime_usage: RuntimeUsage { flow_id, - duration: duration_millis, + duration: duration_micros, }, } } @@ -237,7 +238,7 @@ fn build_execution_result( } fn build_decode_error_result(execution_id: ExecutionId) -> ExecutionResult { - let now = now_unix_ms(); + let now = now_unix_micros(); let runtime_error = RuntimeError::new( "T-TAURUS-000001", "ExecutionFlowDecodeError", @@ -333,6 +334,36 @@ mod tests { } } + #[test] + fn execute_flow_reports_microsecond_timestamps_and_duration() { + let execution_id = ExecutionId::new_v4(); + let fixture = load_fixture("flows/01_return_object.json"); + let flow = execution_flow_from_fixture(fixture); + let engine = ExecutionEngine::new(); + + let run_result = execute_flow(execution_id, flow, &engine, None, None); + + println!( + "started_at={} finished_at={} delta={} runtime_usage.duration={}", + run_result.started_at, + run_result.finished_at, + run_result.finished_at - run_result.started_at, + run_result.runtime_usage.duration + ); + + assert_eq!(run_result.execution_id, execution_id); + assert!(run_result.started_at >= 1_000_000_000_000_000); + assert!(run_result.finished_at >= run_result.started_at); + assert!(run_result.runtime_usage.duration > 0); + assert!( + run_result + .node_execution_results + .iter() + .all(|result| result.started_at >= 1_000_000_000_000_000 + && result.finished_at >= result.started_at) + ); + } + #[test] fn build_execution_result_preserves_node_execution_results() { let execution_id = ExecutionId::new_v4(); From f668d80e5fc1e85100e6daf43f709c83d1cce442 Mon Sep 17 00:00:00 2001 From: Raphael Date: Sat, 6 Jun 2026 21:27:04 +0200 Subject: [PATCH 2/2] fix: changes to time handler --- crates/taurus-core/src/runtime/engine.rs | 2 +- .../src/runtime/execution/value_store.rs | 29 ------------------- crates/taurus-core/src/time.rs | 7 ----- .../src/types/errors/runtime_error.rs | 14 ++++----- 4 files changed, 8 insertions(+), 44 deletions(-) diff --git a/crates/taurus-core/src/runtime/engine.rs b/crates/taurus-core/src/runtime/engine.rs index 08fdb54..4f3e286 100644 --- a/crates/taurus-core/src/runtime/engine.rs +++ b/crates/taurus-core/src/runtime/engine.rs @@ -414,7 +414,7 @@ mod tests { _ctx: &mut ValueStore, _run: &mut ThunkRunner<'_>, ) -> Signal { - std::thread::sleep(Duration::from_millis(2)); + std::thread::sleep(Duration::from_micros(2_000)); Signal::Success(null_value()) } diff --git a/crates/taurus-core/src/runtime/execution/value_store.rs b/crates/taurus-core/src/runtime/execution/value_store.rs index c36f3b0..f511517 100644 --- a/crates/taurus-core/src/runtime/execution/value_store.rs +++ b/crates/taurus-core/src/runtime/execution/value_store.rs @@ -9,7 +9,6 @@ use tucana::shared::{ }; use crate::runtime::execution::trace::{StoreInputSlotEntry, StoreResultEntry, StoreSnapshot}; -use crate::time::now_unix_micros; use crate::types::errors::runtime_error::RuntimeError; #[derive(Clone)] @@ -147,20 +146,6 @@ impl ValueStore { self.flow_input = value; } - pub fn insert_success(&mut self, id: i64, value: Value) { - self.insert_success_with_parameters(id, value, Vec::new()); - } - - pub fn insert_success_with_parameters( - &mut self, - id: i64, - value: Value, - parameter_results: Vec, - ) { - let ts = now_unix_micros(); - self.insert_success_with_timing(id, value, parameter_results, ts, ts); - } - pub fn insert_success_with_timing( &mut self, id: i64, @@ -181,20 +166,6 @@ impl ValueStore { ); } - pub fn insert_error(&mut self, id: i64, runtime_error: RuntimeError) { - self.insert_error_with_parameters(id, runtime_error, Vec::new()); - } - - pub fn insert_error_with_parameters( - &mut self, - id: i64, - runtime_error: RuntimeError, - parameter_results: Vec, - ) { - let ts = now_unix_micros(); - self.insert_error_with_timing(id, runtime_error, parameter_results, ts, ts); - } - pub fn insert_error_with_timing( &mut self, id: i64, diff --git a/crates/taurus-core/src/time.rs b/crates/taurus-core/src/time.rs index 8aab1bd..04fe589 100644 --- a/crates/taurus-core/src/time.rs +++ b/crates/taurus-core/src/time.rs @@ -8,10 +8,3 @@ pub fn now_unix_micros() -> i64 { .map(|it| it.as_micros() as i64) .unwrap_or(0) } - -pub fn now_unix_ms() -> i64 { - SystemTime::now() - .duration_since(UNIX_EPOCH) - .map(|it| it.as_millis() as i64) - .unwrap_or(0) -} diff --git a/crates/taurus-core/src/types/errors/runtime_error.rs b/crates/taurus-core/src/types/errors/runtime_error.rs index afb4e85..bfe6503 100644 --- a/crates/taurus-core/src/types/errors/runtime_error.rs +++ b/crates/taurus-core/src/types/errors/runtime_error.rs @@ -15,7 +15,7 @@ use tucana::shared::{ Error as TucanaError, NumberValue as ProtoNumberValue, Struct, Value, number_value, }; -use crate::time::now_unix_ms; +use crate::time::now_unix_micros; /// Runtime execution failure representation. #[derive(Debug, Clone, PartialEq)] @@ -26,8 +26,8 @@ pub struct RuntimeError { pub category: String, /// Human-readable diagnostic message. pub message: String, - /// Unix timestamp in milliseconds when this error object was created. - pub timestamp_unix_ms: u64, + /// Unix timestamp in microseconds when this error object was created. + pub timestamp_unix_micros: u64, /// Runtime version identifier. pub version: String, /// Dependency versions relevant to this runtime. @@ -47,7 +47,7 @@ impl RuntimeError { code: code.into(), category: category.into(), message: message.into(), - timestamp_unix_ms: now_unix_ms() as u64, + timestamp_unix_micros: now_unix_micros() as u64, version: env!("CARGO_PKG_VERSION").to_string(), dependencies: HashMap::new(), details: HashMap::new(), @@ -112,7 +112,7 @@ impl RuntimeError { Value { kind: Some(NumberValue(ProtoNumberValue { number: Some(number_value::Number::Integer( - self.timestamp_unix_ms as i64, + self.timestamp_unix_micros as i64, )), })), }, @@ -150,7 +150,7 @@ impl RuntimeError { code: self.code.clone(), category: self.category.clone(), message: self.message.clone(), - timestamp: self.timestamp_unix_ms as i64, + timestamp: self.timestamp_unix_micros as i64, version: self.version.clone(), dependencies: self.dependencies.clone(), details: Some(Struct { @@ -165,7 +165,7 @@ impl RuntimeError { code: error.code.clone(), category: error.category.clone(), message: error.message.clone(), - timestamp_unix_ms: error.timestamp.max(0) as u64, + timestamp_unix_micros: error.timestamp.max(0) as u64, version: error.version.clone(), dependencies: error.dependencies.clone(), details: error