1- use std:: time:: Instant ;
2-
31use futures_lite:: StreamExt ;
42use prost:: Message ;
53use taurus_core:: runtime:: engine:: { EmitType , ExecutionEngine , ExecutionId , RespondEmitter } ;
@@ -14,13 +12,15 @@ use tucana::shared::execution_result;
1412use tucana:: shared:: { ExecutionFlow , ExecutionResult , NodeExecutionResult , Value } ;
1513
1614use crate :: client:: runtime_execution:: TaurusRuntimeExecutionService ;
15+ use crate :: telemetry:: metrics;
1716
1817pub fn spawn_worker (
1918 client : async_nats:: Client ,
2019 engine : ExecutionEngine ,
2120 nats_remote : NATSRemoteRuntime ,
2221 runtime_emitter : NATSRespondEmitter ,
2322 mut runtime_execution_service : Option < TaurusRuntimeExecutionService > ,
23+ flow_type : String ,
2424) -> JoinHandle < ( ) > {
2525 tokio:: spawn ( async move {
2626 let mut execution_subscription = match client
@@ -50,6 +50,7 @@ pub fn spawn_worker(
5050 & nats_remote,
5151 & runtime_emitter,
5252 runtime_execution_service. as_mut( ) ,
53+ flow_type. as_str( ) ,
5354 ) . await ;
5455 }
5556 None => {
@@ -71,6 +72,7 @@ async fn process_execution_message(
7172 nats_remote : & NATSRemoteRuntime ,
7273 runtime_emitter : & NATSRespondEmitter ,
7374 mut runtime_execution_service : Option < & mut TaurusRuntimeExecutionService > ,
75+ flow_type : & str ,
7476) {
7577 let requested_execution_id = parse_execution_id_from_subject ( & message. subject , "execution" )
7678 . unwrap_or_else ( || {
@@ -101,6 +103,7 @@ async fn process_execution_message(
101103 } ;
102104
103105 let flow_id = flow. flow_id ;
106+ let function_identifiers = function_identifiers_by_node_id ( & flow) ;
104107 // Taurus app forwards all lifecycle events to emitter.
105108 // Direct request/reply responses remain disabled; delivery is emitter-only.
106109 let respond_emitter = |execution_id, emit_type : EmitType , value : Value | {
@@ -112,6 +115,8 @@ async fn process_execution_message(
112115 engine,
113116 Some ( nats_remote) ,
114117 Some ( & respond_emitter) ,
118+ flow_type,
119+ function_identifiers,
115120 )
116121 . await ;
117122 log:: debug!(
@@ -153,9 +158,12 @@ async fn execute_flow(
153158 engine : & ExecutionEngine ,
154159 remote : Option < & dyn RemoteRuntime > ,
155160 respond_emitter : Option < & dyn RespondEmitter > ,
161+ flow_type : & str ,
162+ function_identifiers : std:: collections:: HashMap < i64 , String > ,
156163) -> FlowRunResult {
157164 let started_at = now_unix_micros ( ) ;
158165 let flow_id = flow. flow_id ;
166+ let project_id = flow. project_id ;
159167 let input = flow. input_value . clone ( ) ;
160168 let report = engine
161169 . execute_flow_with_execution_id_report_async (
@@ -167,6 +175,16 @@ async fn execute_flow(
167175 )
168176 . await ;
169177 let finished_at = now_unix_micros ( ) ;
178+ record_flow_metrics (
179+ flow_id,
180+ project_id,
181+ flow_type,
182+ started_at,
183+ finished_at,
184+ & report. signal ,
185+ & report. node_execution_results ,
186+ & function_identifiers,
187+ ) ;
170188
171189 FlowRunResult {
172190 execution_id,
@@ -179,6 +197,67 @@ async fn execute_flow(
179197 }
180198}
181199
200+ fn function_identifiers_by_node_id ( flow : & ExecutionFlow ) -> std:: collections:: HashMap < i64 , String > {
201+ flow. node_functions
202+ . iter ( )
203+ . filter_map ( |function| {
204+ function
205+ . database_id
206+ . map ( |id| ( id, function. runtime_function_id . clone ( ) ) )
207+ } )
208+ . collect ( )
209+ }
210+
211+ fn record_flow_metrics (
212+ flow_id : i64 ,
213+ project_id : i64 ,
214+ flow_type : & str ,
215+ started_at : i64 ,
216+ finished_at : i64 ,
217+ signal : & Signal ,
218+ node_execution_results : & [ NodeExecutionResult ] ,
219+ function_identifiers : & std:: collections:: HashMap < i64 , String > ,
220+ ) {
221+ metrics:: flow_execution ( metrics:: FlowExecution {
222+ flow_id,
223+ project_id,
224+ flow_type,
225+ outcome : signal_outcome ( signal) ,
226+ duration_seconds : metrics:: duration_seconds ( started_at, finished_at) ,
227+ } ) ;
228+
229+ for result in node_execution_results {
230+ let node_id = metrics:: result_node_id ( result) ;
231+ let function_identifier = metrics:: result_function_identifier ( result)
232+ . map ( ToOwned :: to_owned)
233+ . or_else ( || node_id. and_then ( |id| function_identifiers. get ( & id) . cloned ( ) ) )
234+ . unwrap_or_else ( || "unknown" . to_string ( ) ) ;
235+ let ( error_code, error_category) = metrics:: node_result_error ( result) ;
236+
237+ metrics:: function_execution ( metrics:: FunctionExecution {
238+ flow_id,
239+ project_id,
240+ flow_type,
241+ function_identifier : function_identifier. as_str ( ) ,
242+ node_id,
243+ outcome : metrics:: node_result_outcome ( result) ,
244+ duration_seconds : metrics:: duration_seconds ( result. started_at , result. finished_at ) ,
245+ error_code,
246+ error_category,
247+ } ) ;
248+ }
249+ }
250+
251+ fn signal_outcome ( signal : & Signal ) -> & ' static str {
252+ match signal {
253+ Signal :: Success ( _) => "success" ,
254+ Signal :: Failure ( _) => "failure" ,
255+ Signal :: Return ( _) => "return" ,
256+ Signal :: Respond ( _) => "respond" ,
257+ Signal :: Stop => "stop" ,
258+ }
259+ }
260+
182261fn parse_execution_id_from_subject (
183262 subject : & async_nats:: Subject ,
184263 prefix : & str ,
@@ -327,7 +406,17 @@ mod tests {
327406 let flow = execution_flow_from_fixture ( fixture) ;
328407 let engine = ExecutionEngine :: new ( ) ;
329408
330- let run_result = execute_flow ( execution_id, flow, & engine, None , None ) . await ;
409+ let function_identifiers = function_identifiers_by_node_id ( & flow) ;
410+ let run_result = execute_flow (
411+ execution_id,
412+ flow,
413+ & engine,
414+ None ,
415+ None ,
416+ "test" ,
417+ function_identifiers,
418+ )
419+ . await ;
331420
332421 println ! (
333422 "started_at={} finished_at={} delta={}" ,
0 commit comments