Skip to content

Execution Dependency Rules

This document describes how FlowDrop determines when a node is ready to execute based on its incoming edges (dependencies).

Overview

FlowDrop workflows consist of nodes connected by edges. Edges can be of different types:

Edge Type Purpose Controls
Trigger Controls execution flow Whether a node executes at all
Data Passes data between nodes What input data a node receives

The dependency rules determine: 1. When a node can execute (dependency satisfaction) 2. What data the node receives (data resolution)

Edge Types

Trigger Edges

Trigger edges connect to a node's trigger input port (typically named "trigger"). They control whether a node executes.

Handle format: {nodeId}-input-trigger

Examples: - Gateway branches (If/Else, A/B, Switch) - Sequential flow control - Event-driven execution

Data Edges

Data edges connect to a node's data input ports (any non-trigger port). They pass data from one node's output to another node's input.

Handle format: {nodeId}-input-{portName} (e.g., logger.1-input-message)

Loopback Edges

Loopback edges connect to a node's loopback input port (e.g., loop_back on ForEach nodes). They represent loop-back connections for iterative processing.

Handle format: {nodeId}-input-loop_back

Special behavior: - Excluded from dependency checking - they don't block node execution - Excluded from cycle detection - allows visual loops without breaking topological sort - Rendered with dotted lines in the workflow UI - Used primarily by iterator nodes (ForEach) to receive results from loop body

Dependency Rules

Rule 1: Trigger Edges Use OR Logic

When a node has trigger edges, it executes when at least ONE trigger source is satisfied.

┌─────────────┐
│  Gateway    │
├─────────────┤
│ Branch A ───┼──trigger──┐
│ Branch B ───┼──trigger──┼──► [Node X]
└─────────────┘           │
                          │
[Other Node] ──trigger────┘

Node X executes when:
- Branch A is active, OR
- Branch B is active, OR  
- Other Node completes

Rationale: Trigger edges typically come from branching logic where only one path is active at a time.

Rule 2: Data Edges to Different Ports Use AND Logic

When a node has data edges targeting different input ports, ALL ports must have at least one completed source.

[Source A] ──data──► port_x ─┐
                             ├──► [Compare Node]
[Source B] ──data──► port_y ─┘

Compare Node executes when:
- Source A completes (provides port_x), AND
- Source B completes (provides port_y)

Rationale: If a node declares multiple distinct inputs, it likely needs all of them to function correctly.

Rule 3: Data Edges to Same Port Use OR Logic

When multiple data edges target the same input port, at least ONE source must complete.

[Template A] ──data──► message ─┐
                                ├──► [Logger]
[Template B] ──data──► message ─┘

Logger executes when:
- Template A completes, OR
- Template B completes

Rationale: Multiple sources providing the same type of data (same port) typically represent alternative paths—only one value is needed.

Rule 4: Trigger Edges Take Priority

When a node has both trigger and data edges, only the trigger edges control execution timing. Data edges are used solely for data resolution.

[Gateway] ──trigger──────────────────► [Process Node]
                                            ↑
[Data Source] ──data──► input_data ─────────┘

Process Node executes when:
- Gateway trigger is satisfied

Data from Data Source is gathered IF it's available, but doesn't block execution.

Combined Rules Summary

Scenario Logic Description
Multiple triggers OR Execute when ANY trigger is satisfied
Multiple data → same port OR Execute when ANY source for that port completes
Multiple data → different ports AND Execute when ALL ports have at least one source
Trigger + Data edges Trigger controls Data edges don't block; just gather available data
Loopback edges Excluded Never block execution; used for loop iteration

Visual Examples

Example 1: Branching Workflow (Works Correctly)

                    ┌──► [Template A] ──┐
[A/B Gateway] ──────┤                   ├──► [Logger] (message port)
                    └──► [Template B] ──┘
  • A/B Gateway activates either branch A or B
  • Template A or Template B executes (not both)
  • Logger has two data edges to the same message port
  • Logger executes when either Template completes (OR logic for same port)

Example 2: Parallel Inputs (Waits for All)

[User Input] ──► name ───┐
                         ├──► [Greeting Generator]
[Time Service] ──► time ─┘
  • Greeting Generator has two different ports: name and time
  • Generator waits for BOTH sources (AND logic for different ports)

Example 3: Mixed Trigger and Data

[Button Click] ──trigger──────────────────► [Send Email]
                                                 ↑
[Draft Email] ──data──► email_content ───────────┘
  • Send Email has a trigger input and a data input
  • Executes when: Button Click happens (trigger satisfied)
  • Data: Gathers from Draft Email if available, but doesn't wait for it

Data Resolution

When a node executes, it receives data from completed upstream nodes:

Same Port Resolution (Multiple Sources)

When multiple sources target the same port and more than one has completed, the system uses the most recently completed source's value:

  1. Primary: Latest execution order (higher counter = more recent)
  2. Fallback: Alphabetical by node ID

A warning is logged when this occurs:

Multiple sources target the same input port 'message' on node 'logger.1'.
Previous source: 'template_a.1', current source: 'template_b.1'.
Using value from 'template_b.1' (resolved by latest execution order).

Missing Data

If a source node hasn't completed (e.g., branch not taken), its data is simply not included. The node executes with whatever data IS available.

Orchestrator-Specific Behavior

Pipeline-Based Orchestrators

SynchronousPipelineOrchestrator and queue-based execution use JobGenerationService::areJobDependenciesMet() to determine job readiness:

  • Checks trigger satisfaction (OR logic)
  • Checks data dependencies per-port (OR within port, AND across ports)
  • Jobs are queued when ready

Direct Orchestrator

SynchronousOrchestrator uses pre-computed topological order:

  • Nodes execute in dependency order
  • shouldExecuteNode() checks trigger conditions
  • Data is gathered from whatever executed parents are available
  • Skipped nodes don't block downstream execution

Best Practices

DO: Use Same Port for Alternative Sources

[Option A] ──► input ─┐
                      ├──► [Processor]  ✅ Works with branching
[Option B] ──► input ─┘

DO: Use Different Ports for Required Parallel Data

[Source X] ──► port_a ─┐
                       ├──► [Combiner]  ✅ Waits for both
[Source Y] ──► port_b ─┘

DON'T: Connect Mutually Exclusive Sources to Different Ports

[Branch A] ──► port_a ─┐
                       ├──► [Node]  ❌ Will wait forever if Branch B taken
[Branch B] ──► port_b ─┘

Solution: Use same port or add trigger edges.