@@ -2,6 +2,8 @@ use crate::prelude::*;
22
33use anyhow:: { Context , Ok } ;
44use futures:: future:: try_join_all;
5+ use log:: warn;
6+ use tokio:: time:: Duration ;
57
68use crate :: base:: value:: EstimatedByteSize ;
79use crate :: builder:: { AnalyzedTransientFlow , plan:: * } ;
@@ -368,10 +370,13 @@ async fn evaluate_op_scope(
368370 for reactive_op in op_scope. reactive_ops . iter ( ) {
369371 match reactive_op {
370372 AnalyzedReactiveOp :: Transform ( op) => {
373+ let transform_key = format ! ( "transform/{}{}" , op_scope. scope_qualifier, op. name) ;
374+
375+ // eprintln!("🔍 DEBUG: Transform op '{}' (function: {}) starting, timeout: {:?}",
376+ // op.name, op.op_kind, op.function_exec_info.timeout);
377+
371378 // Track transform operation start
372379 if let Some ( ref op_stats) = operation_in_process_stats {
373- let transform_key =
374- format ! ( "transform/{}{}" , op_scope. scope_qualifier, op. name) ;
375380 op_stats. start_processing ( & transform_key, 1 ) ;
376381 }
377382
@@ -380,6 +385,28 @@ async fn evaluate_op_scope(
380385 input_values. push ( value?) ;
381386 }
382387
388+ let timeout_duration = op
389+ . function_exec_info
390+ . timeout
391+ . unwrap_or ( Duration :: from_secs ( 300 ) ) ;
392+ let warn_duration = Duration :: from_secs ( 30 ) ;
393+
394+ let op_name_for_warning = op. name . clone ( ) ;
395+ let op_kind_for_warning = op. op_kind . clone ( ) ;
396+ let warn_handle = tokio:: spawn ( async move {
397+ tokio:: time:: sleep ( warn_duration) . await ;
398+ // eprintln!("WARNING: Function '{}' is taking longer than 30s", op_name_for_warning);
399+ // warn!("Function '{}' is taking longer than 30s", op_name_for_warning);
400+ eprintln ! (
401+ "⚠️ WARNING: Function '{}' ({}) is taking longer than 30s" ,
402+ op_kind_for_warning, op_name_for_warning
403+ ) ; // ✅ Show both
404+ warn ! (
405+ "Function '{}' ({}) is taking longer than 30s" ,
406+ op_kind_for_warning, op_name_for_warning
407+ ) ;
408+ } ) ;
409+ // Execute with timeout
383410 let result = if op. function_exec_info . enable_cache {
384411 let output_value_cell = memory. get_cache_entry (
385412 || {
@@ -393,27 +420,88 @@ async fn evaluate_op_scope(
393420 & op. function_exec_info . output_type ,
394421 /*ttl=*/ None ,
395422 ) ?;
396- evaluate_with_cell ( output_value_cell. as_ref ( ) , move || {
423+
424+ let eval_future = evaluate_with_cell ( output_value_cell. as_ref ( ) , move || {
397425 op. executor . evaluate ( input_values)
398- } )
399- . await
400- . and_then ( |v| head_scope. define_field ( & op. output , & v) )
426+ } ) ;
427+
428+ // Handle timeout
429+ let timeout_result = tokio:: time:: timeout ( timeout_duration, eval_future) . await ;
430+ if timeout_result. is_err ( ) {
431+ Err ( anyhow ! (
432+ // "Function '{}' timed out after {} seconds",
433+ "Function '{}' ({}) timed out after {} seconds" ,
434+ op. op_kind,
435+ op. name,
436+ timeout_duration. as_secs( )
437+ ) )
438+ } else {
439+ timeout_result
440+ . unwrap ( )
441+ . and_then ( |v| head_scope. define_field ( & op. output , & v) )
442+ }
401443 } else {
402- op. executor
403- . evaluate ( input_values)
404- . await
405- . and_then ( |v| head_scope. define_field ( & op. output , & v) )
406- }
407- . with_context ( || format ! ( "Evaluating Transform op `{}`" , op. name, ) ) ;
444+ let eval_future = op. executor . evaluate ( input_values) ;
445+
446+ // Handle timeout
447+ let timeout_result = tokio:: time:: timeout ( timeout_duration, eval_future) . await ;
448+ if timeout_result. is_err ( ) {
449+ Err ( anyhow ! (
450+ // "Function '{}' timed out after {} seconds",
451+ "Function '{}' ({}) timed out after {} seconds" ,
452+ op. op_kind,
453+ op. name,
454+ timeout_duration. as_secs( )
455+ ) )
456+ } else {
457+ timeout_result
458+ . unwrap ( )
459+ . and_then ( |v| head_scope. define_field ( & op. output , & v) )
460+ }
461+ } ;
462+
463+ warn_handle. abort ( ) ;
408464
409465 // Track transform operation completion
410466 if let Some ( ref op_stats) = operation_in_process_stats {
411- let transform_key =
412- format ! ( "transform/{}{}" , op_scope. scope_qualifier, op. name) ;
413467 op_stats. finish_processing ( & transform_key, 1 ) ;
414468 }
415469
416- result?
470+ result. with_context ( || format ! ( "Evaluating Transform op `{}`" , op. name) ) ?
471+ // let result = if op.function_exec_info.enable_cache {
472+ // let output_value_cell = memory.get_cache_entry(
473+ // || {
474+ // Ok(op
475+ // .function_exec_info
476+ // .fingerprinter
477+ // .clone()
478+ // .with(&input_values)?
479+ // .into_fingerprint())
480+ // },
481+ // &op.function_exec_info.output_type,
482+ // /*ttl=*/ None,
483+ // )?;
484+ // evaluate_with_cell(output_value_cell.as_ref(), move || {
485+ // op.executor.evaluate(input_values)
486+ // })
487+ // .await
488+ // .and_then(|v| head_scope.define_field(&op.output, &v))
489+ // } else {
490+ // op.executor
491+ // .evaluate(input_values)
492+ // .await
493+ // .and_then(|v| head_scope.define_field(&op.output, &v))
494+ // }
495+ // .with_context(|| format!("Evaluating Transform op `{}`", op.name,));
496+
497+ // // Track transform operation completion
498+ // if let Some(ref op_stats) = operation_in_process_stats {
499+ // let transform_key =
500+ // format!("transform/{}{}", op_scope.scope_qualifier, op.name);
501+ // op_stats.finish_processing(&transform_key, 1);
502+ // }
503+
504+ // result?
417505 }
418506
419507 AnalyzedReactiveOp :: ForEach ( op) => {
0 commit comments