@@ -312,6 +312,7 @@ pub struct DFComponentGraph {
312312 pub needed_map : HashMap < Handle , Vec < NodeIndex > > ,
313313 pub produced : HashMap < Handle , Vec < ( NodeIndex , Handle ) > > ,
314314 pub results : Vec < DFGTxResult > ,
315+ deferred_dependences : Vec < ( NodeIndex , NodeIndex , Handle ) > ,
315316}
316317impl DFComponentGraph {
317318 pub fn build ( & mut self , nodes : & mut Vec < ComponentNode > ) -> Result < ( ) > {
@@ -350,7 +351,16 @@ impl DFComponentGraph {
350351 error ! ( target: "scheduler" , { output_handle = ?hex:: encode( i. clone( ) ) } ,
351352 "Missing producer for handle" ) ;
352353 } else {
353- dependence_pairs. push ( ( producer[ 0 ] . 0 , consumer) ) ;
354+ // Cross-transaction dependence: defer until
355+ // after DB fetch. If the handle is found in
356+ // DB, we use the fetched value and skip the
357+ // dependence edge.
358+ self . deferred_dependences
359+ . push ( ( producer[ 0 ] . 0 , consumer, i. clone ( ) ) ) ;
360+ self . needed_map
361+ . entry ( i. clone ( ) )
362+ . and_modify ( |uses| uses. push ( consumer) )
363+ . or_insert ( vec ! [ consumer] ) ;
354364 }
355365 } else {
356366 self . needed_map
@@ -361,19 +371,52 @@ impl DFComponentGraph {
361371 }
362372 }
363373
364- // We build a replica of the graph and map it to the
365- // underlying DiGraph so we can identify cycles.
366- let mut digraph = self . graph . map ( |idx , _| idx , |_ , _| ( ) ) . graph ( ) . clone ( ) ;
367- // Add transaction dependence edges
374+ // Same-transaction dependences are always acyclic (they
375+ // derive from the transaction's internal DAG). Add them
376+ // directly; cycle detection runs once in
377+ // resolve_dependences() over the full edge set.
368378 for ( producer, consumer) in dependence_pairs. iter ( ) {
379+ if self . graph . add_edge ( * producer, * consumer, ( ) ) . is_err ( ) {
380+ let prod = self
381+ . graph
382+ . node_weight ( * producer)
383+ . ok_or ( SchedulerError :: DataflowGraphError ) ?;
384+ let cons = self
385+ . graph
386+ . node_weight ( * consumer)
387+ . ok_or ( SchedulerError :: DataflowGraphError ) ?;
388+ error ! ( target: "scheduler" , { producer_id = ?hex:: encode( prod. transaction_id. clone( ) ) , consumer_id = ?hex:: encode( cons. transaction_id. clone( ) ) } ,
389+ "Unexpected cycle in same-transaction dependence" ) ;
390+ return Err ( SchedulerError :: CyclicDependence . into ( ) ) ;
391+ }
392+ }
393+ Ok ( ( ) )
394+ }
395+
396+ // Resolve deferred cross-transaction dependences after DB fetch.
397+ // Dependences whose handle was successfully fetched are dropped
398+ // (the consumer already has the data). Remaining dependences are
399+ // added as graph edges after cycle detection.
400+ pub fn resolve_dependences ( & mut self , fetched_handles : & HashSet < Handle > ) -> Result < ( ) > {
401+ let remaining: Vec < ( NodeIndex , NodeIndex ) > = self
402+ . deferred_dependences
403+ . drain ( ..)
404+ . filter ( |( _, _, handle) | !fetched_handles. contains ( handle) )
405+ . map ( |( prod, cons, _) | ( prod, cons) )
406+ . collect ( ) ;
407+ if remaining. is_empty ( ) {
408+ return Ok ( ( ) ) ;
409+ }
410+ // Build a digraph replica including existing edges +
411+ // remaining deferred edges and check for cycles
412+ let mut digraph = self . graph . map ( |idx, _| idx, |_, _| ( ) ) . graph ( ) . clone ( ) ;
413+ for ( producer, consumer) in remaining. iter ( ) {
369414 digraph. add_edge ( * producer, * consumer, ( ) ) ;
370415 }
371416 let mut tarjan = daggy:: petgraph:: algo:: TarjanScc :: new ( ) ;
372417 let mut sccs = Vec :: new ( ) ;
373418 tarjan. run ( & digraph, |scc| {
374419 if scc. len ( ) > 1 {
375- // All non-singleton SCCs in a directed graph are
376- // dependence cycles
377420 sccs. push ( scc. to_vec ( ) ) ;
378421 }
379422 } ) ;
@@ -389,9 +432,6 @@ impl DFComponentGraph {
389432 . graph
390433 . node_weight_mut ( * idx)
391434 . ok_or ( SchedulerError :: DataflowGraphError ) ?;
392- // Mark the node as uncomputable so we don't go
393- // and mark as completed operations that are in
394- // error.
395435 tx. is_uncomputable = true ;
396436 error ! ( target: "scheduler" , { transaction_id = ?hex:: encode( tx. transaction_id. clone( ) ) } ,
397437 "Transaction is part of a dependence cycle" ) ;
@@ -405,26 +445,20 @@ impl DFComponentGraph {
405445 }
406446 }
407447 return Err ( SchedulerError :: CyclicDependence . into ( ) ) ;
408- } else {
409- // If no dependence cycles were found, then we can
410- // complete the graph and proceed to execution
411- for ( producer, consumer) in dependence_pairs. iter ( ) {
412- // The error case here should not happen as we've
413- // already covered it by testing for SCCs in the graph
414- // first
415- if self . graph . add_edge ( * producer, * consumer, ( ) ) . is_err ( ) {
416- let prod = self
417- . graph
418- . node_weight ( * producer)
419- . ok_or ( SchedulerError :: DataflowGraphError ) ?;
420- let cons = self
421- . graph
422- . node_weight ( * consumer)
423- . ok_or ( SchedulerError :: DataflowGraphError ) ?;
424- error ! ( target: "scheduler" , { producer_id = ?hex:: encode( prod. transaction_id. clone( ) ) , consumer_id = ?hex:: encode( cons. transaction_id. clone( ) ) } ,
448+ }
449+ for ( producer, consumer) in remaining. iter ( ) {
450+ if self . graph . add_edge ( * producer, * consumer, ( ) ) . is_err ( ) {
451+ let prod = self
452+ . graph
453+ . node_weight ( * producer)
454+ . ok_or ( SchedulerError :: DataflowGraphError ) ?;
455+ let cons = self
456+ . graph
457+ . node_weight ( * consumer)
458+ . ok_or ( SchedulerError :: DataflowGraphError ) ?;
459+ error ! ( target: "scheduler" , { producer_id = ?hex:: encode( prod. transaction_id. clone( ) ) , consumer_id = ?hex:: encode( cons. transaction_id. clone( ) ) } ,
425460 "Dependence cycle when adding dependence - initial cycle detection failed" ) ;
426- return Err ( SchedulerError :: CyclicDependence . into ( ) ) ;
427- }
461+ return Err ( SchedulerError :: CyclicDependence . into ( ) ) ;
428462 }
429463 }
430464 Ok ( ( ) )
0 commit comments