@@ -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 :: base:: { schema, value} ;
@@ -366,10 +368,13 @@ async fn evaluate_op_scope(
366368 for reactive_op in op_scope. reactive_ops . iter ( ) {
367369 match reactive_op {
368370 AnalyzedReactiveOp :: Transform ( op) => {
371+ let transform_key = format ! ( "transform/{}{}" , op_scope. scope_qualifier, op. name) ;
372+
373+ // eprintln!("🔍 DEBUG: Transform op '{}' (function: {}) starting, timeout: {:?}",
374+ // op.name, op.op_kind, op.function_exec_info.timeout);
375+
369376 // Track transform operation start
370377 if let Some ( ref op_stats) = operation_in_process_stats {
371- let transform_key =
372- format ! ( "transform/{}{}" , op_scope. scope_qualifier, op. name) ;
373378 op_stats. start_processing ( & transform_key, 1 ) ;
374379 }
375380
@@ -378,6 +383,28 @@ async fn evaluate_op_scope(
378383 input_values. push ( value?) ;
379384 }
380385
386+ let timeout_duration = op
387+ . function_exec_info
388+ . timeout
389+ . unwrap_or ( Duration :: from_secs ( 300 ) ) ;
390+ let warn_duration = Duration :: from_secs ( 30 ) ;
391+
392+ let op_name_for_warning = op. name . clone ( ) ;
393+ let op_kind_for_warning = op. op_kind . clone ( ) ;
394+ let warn_handle = tokio:: spawn ( async move {
395+ tokio:: time:: sleep ( warn_duration) . await ;
396+ // eprintln!("WARNING: Function '{}' is taking longer than 30s", op_name_for_warning);
397+ // warn!("Function '{}' is taking longer than 30s", op_name_for_warning);
398+ eprintln ! (
399+ "⚠️ WARNING: Function '{}' ({}) is taking longer than 30s" ,
400+ op_kind_for_warning, op_name_for_warning
401+ ) ; // ✅ Show both
402+ warn ! (
403+ "Function '{}' ({}) is taking longer than 30s" ,
404+ op_kind_for_warning, op_name_for_warning
405+ ) ;
406+ } ) ;
407+ // Execute with timeout
381408 let result = if op. function_exec_info . enable_cache {
382409 let output_value_cell = memory. get_cache_entry (
383410 || {
@@ -391,27 +418,88 @@ async fn evaluate_op_scope(
391418 & op. function_exec_info . output_type ,
392419 /*ttl=*/ None ,
393420 ) ?;
394- evaluate_with_cell ( output_value_cell. as_ref ( ) , move || {
421+
422+ let eval_future = evaluate_with_cell ( output_value_cell. as_ref ( ) , move || {
395423 op. executor . evaluate ( input_values)
396- } )
397- . await
398- . and_then ( |v| head_scope. define_field ( & op. output , & v) )
424+ } ) ;
425+
426+ // Handle timeout
427+ let timeout_result = tokio:: time:: timeout ( timeout_duration, eval_future) . await ;
428+ if timeout_result. is_err ( ) {
429+ Err ( anyhow ! (
430+ // "Function '{}' timed out after {} seconds",
431+ "Function '{}' ({}) timed out after {} seconds" ,
432+ op. op_kind,
433+ op. name,
434+ timeout_duration. as_secs( )
435+ ) )
436+ } else {
437+ timeout_result
438+ . unwrap ( )
439+ . and_then ( |v| head_scope. define_field ( & op. output , & v) )
440+ }
399441 } else {
400- op. executor
401- . evaluate ( input_values)
402- . await
403- . and_then ( |v| head_scope. define_field ( & op. output , & v) )
404- }
405- . with_context ( || format ! ( "Evaluating Transform op `{}`" , op. name, ) ) ;
442+ let eval_future = op. executor . evaluate ( input_values) ;
443+
444+ // Handle timeout
445+ let timeout_result = tokio:: time:: timeout ( timeout_duration, eval_future) . await ;
446+ if timeout_result. is_err ( ) {
447+ Err ( anyhow ! (
448+ // "Function '{}' timed out after {} seconds",
449+ "Function '{}' ({}) timed out after {} seconds" ,
450+ op. op_kind,
451+ op. name,
452+ timeout_duration. as_secs( )
453+ ) )
454+ } else {
455+ timeout_result
456+ . unwrap ( )
457+ . and_then ( |v| head_scope. define_field ( & op. output , & v) )
458+ }
459+ } ;
460+
461+ warn_handle. abort ( ) ;
406462
407463 // Track transform operation completion
408464 if let Some ( ref op_stats) = operation_in_process_stats {
409- let transform_key =
410- format ! ( "transform/{}{}" , op_scope. scope_qualifier, op. name) ;
411465 op_stats. finish_processing ( & transform_key, 1 ) ;
412466 }
413467
414- result?
468+ result. with_context ( || format ! ( "Evaluating Transform op `{}`" , op. name) ) ?
469+ // let result = if op.function_exec_info.enable_cache {
470+ // let output_value_cell = memory.get_cache_entry(
471+ // || {
472+ // Ok(op
473+ // .function_exec_info
474+ // .fingerprinter
475+ // .clone()
476+ // .with(&input_values)?
477+ // .into_fingerprint())
478+ // },
479+ // &op.function_exec_info.output_type,
480+ // /*ttl=*/ None,
481+ // )?;
482+ // evaluate_with_cell(output_value_cell.as_ref(), move || {
483+ // op.executor.evaluate(input_values)
484+ // })
485+ // .await
486+ // .and_then(|v| head_scope.define_field(&op.output, &v))
487+ // } else {
488+ // op.executor
489+ // .evaluate(input_values)
490+ // .await
491+ // .and_then(|v| head_scope.define_field(&op.output, &v))
492+ // }
493+ // .with_context(|| format!("Evaluating Transform op `{}`", op.name,));
494+
495+ // // Track transform operation completion
496+ // if let Some(ref op_stats) = operation_in_process_stats {
497+ // let transform_key =
498+ // format!("transform/{}{}", op_scope.scope_qualifier, op.name);
499+ // op_stats.finish_processing(&transform_key, 1);
500+ // }
501+
502+ // result?
415503 }
416504
417505 AnalyzedReactiveOp :: ForEach ( op) => {
0 commit comments