Execution Control
Advanced control over how your workflows execute.
Parallel Execution
Execute the same workflow with different parameters in parallel.
Basic Usage
yaml
steps:
- name: process-files
run: file-processor
parallel:
items:
- "file1.csv"
- "file2.csv"
- "file3.csv"
params: "FILE=${ITEM}"
---
name: file-processor
params:
- FILE: ""
steps:
- name: process
command: python process.py --file ${FILE}
With Concurrency Control
yaml
steps:
- name: process-files
run: file-processor
parallel:
items: ${FILE_LIST}
maxConcurrent: 2 # Process max 2 files at a time
params: "FILE=${ITEM}"
Dynamic Items
yaml
steps:
- name: find-files
command: find /data -name "*.csv" -type f
output: CSV_FILES
- name: process-files
run: file-processor
parallel: ${CSV_FILES}
params: "FILE=${ITEM}"
Capturing Output
yaml
steps:
- name: parallel-tasks
run: task-processor
parallel:
items: [1, 2, 3]
output: RESULTS
- name: summary
command: |
echo "Total: ${RESULTS.summary.total}"
echo "Succeeded: ${RESULTS.summary.succeeded}"
echo "Failed: ${RESULTS.summary.failed}"
Output structure:
json
{
"summary": {
"total": 3,
"succeeded": 3,
"failed": 0
},
"outputs": [
{"RESULT": "output1"},
{"RESULT": "output2"},
{"RESULT": "output3"}
]
}
Maximum Active Steps
Control how many steps run concurrently:
yaml
maxActiveSteps: 2 # Run up to 2 steps in parallel
steps:
- name: task1
command: ./task1.sh
depends: [] # Explicitly declare no dependency
- name: task2
command: ./task2.sh
depends: []
- name: task3
command: ./task3.sh
depends: []
- name: task4
command: ./task4.sh
depends: []
# All start in parallel, limited by maxActiveSteps
Maximum Active Runs
Control concurrent workflow instances:
yaml
maxActiveRuns: 1 # Only one instance at a time
schedule: "*/5 * * * *" # Every 5 minutes
Options:
1
: Only one instance (default)N
: Allow N concurrent instances-1
: Unlimited instances
Queue Management
Assign workflows to queues:
yaml
queue: "batch"
maxActiveRuns: 2
Manual queue control:
bash
# Enqueue with custom ID
dagu enqueue workflow.yaml --run-id=custom-id
# Remove from queue
dagu dequeue --dag-run=workflow:custom-id
Timeout Control
Set execution time limits:
Workflow Timeout
yaml
timeoutSec: 3600 # 1 hour timeout
steps:
- name: long-task
command: ./process.sh
Cleanup Timeout
yaml
maxCleanUpTimeSec: 300 # 5 minutes for cleanup
handlerOn:
exit:
command: ./cleanup.sh # Must finish within 5 minutes
Initial Delay
Delay workflow start:
yaml
delaySec: 60 # Wait 60 seconds before starting
steps:
- name: delayed-task
command: ./task.sh
Execution Order
Sequential Execution
yaml
steps:
- name: first
command: echo "1"
- name: second
command: echo "2"
depends: first
- name: third
command: echo "3"
depends: second
Parallel with Dependencies
yaml
steps:
- name: setup
command: ./setup.sh
- name: task-a
command: ./task-a.sh
depends: setup
- name: task-b
command: ./task-b.sh
depends: setup
- name: finalize
command: ./finalize.sh
depends:
- task-a
- task-b
Retry and Repeat Control
Exponential Backoff
Control retry and repeat intervals with exponential backoff to avoid overwhelming systems:
Retry with Backoff
yaml
steps:
# API call with exponential backoff
- name: resilient-api-call
command: curl https://api.example.com/data
retryPolicy:
limit: 6
intervalSec: 1
backoff: 2.0 # Double interval each time
maxIntervalSec: 60 # Cap at 60 seconds
exitCode: [429, 503] # Rate limit or service unavailable
# Intervals: 1s, 2s, 4s, 8s, 16s, 32s → 60s
Repeat with Backoff
yaml
steps:
# Service health check with backoff
- name: wait-for-healthy
command: ./health-check.sh
output: STATUS
repeatPolicy:
repeat: until
condition: "${STATUS}"
expected: "healthy"
intervalSec: 2
backoff: 1.5 # Gentler backoff (1.5x)
maxIntervalSec: 120 # Cap at 2 minutes
limit: 50
# Intervals: 2s, 3s, 4.5s, 6.75s, 10.125s...
Practical Examples
Database Connection Retry:
yaml
steps:
- name: connect-db
command: psql -h db.example.com -c "SELECT 1"
retryPolicy:
limit: 10
intervalSec: 0.5
backoff: true # true = 2.0 multiplier
maxIntervalSec: 30
# Quick initial retries, backing off to 30s max
Service Startup Monitoring:
yaml
steps:
- name: start-service
command: systemctl start myservice
- name: wait-for-ready
command: systemctl is-active myservice
repeatPolicy:
repeat: until
exitCode: [0] # Exit 0 means service is active
intervalSec: 1
backoff: 2.0
maxIntervalSec: 60
limit: 30
# Check frequently at first, then less often
API Polling with Rate Limit Awareness:
yaml
steps:
- name: poll-job-status
command: |
response=$(curl -s https://api.example.com/job/123)
echo "$response" | jq -r '.status'
output: JOB_STATUS
repeatPolicy:
repeat: until
condition: "${JOB_STATUS}"
expected: "re:completed|failed"
intervalSec: 5
backoff: 1.5
maxIntervalSec: 300 # Max 5 minutes between checks
limit: 100
Backoff Benefits
- Resource Efficiency: Reduces load on failing services
- Cost Optimization: Fewer API calls means lower costs
- Better Recovery: Gives services time to recover
- Rate Limit Compliance: Naturally backs off when hitting limits
- Network Stability: Reduces network congestion during outages
Configuration Tips
- Start Small: Begin with short intervals for quick recovery
- Choose Multipliers:
2.0
: Standard exponential (1, 2, 4, 8...)1.5
: Gentler increase (1, 1.5, 2.25...)3.0
: Aggressive backoff (1, 3, 9, 27...)
- Set Caps: Always use
maxIntervalSec
to prevent excessive waits - Consider Limits: Set reasonable retry/repeat limits
See Also
- Error Handling - Handle failures gracefully
- Scheduling - Schedule workflow execution
- Queues - Detailed queue management