Execution Model
Understanding Probe’s execution model is crucial for designing efficient workflows and troubleshooting execution issues. This guide explores how Probe schedules, executes, and manages workflow components from start to finish.
Execution Overview
Probe follows a structured execution model that processes workflows in a predictable, deterministic manner:
- Workflow Parsing: Parse and validate YAML configuration
- Dependency Resolution: Build execution graph based on job dependencies
- Job Scheduling: Schedule jobs for execution based on dependencies
- Step Execution: Execute steps sequentially within each job
- State Management: Track execution state and results
- Resource Cleanup: Clean up resources after execution
Execution Hierarchy
Workflow
├── Job 1 (independent)
│ ├── Step 1.1 (sequential)
│ ├── Step 1.2 (sequential)
│ └── Step 1.3 (sequential)
├── Job 2 (independent, parallel to Job 1)
│ ├── Step 2.1 (sequential)
│ └── Step 2.2 (sequential)
└── Job 3 (depends on Job 1 and Job 2)
├── Step 3.1 (sequential)
└── Step 3.2 (sequential)Job Execution Model
Independent Job Execution
Jobs without dependencies execute in parallel:
name: Parallel Service Check
description: Check multiple services simultaneously
jobs:
- id: database-check
name: Database Health
steps:
- name: Check Database
uses: http
with:
method: GET
url: "{{vars.DB_URL}}/health"
test: res.code == 200
- id: api-check
name: API Health
steps:
- name: Check API
uses: http
with:
method: GET
url: "{{vars.API_URL}}/health"
test: res.code == 200
- id: cache-check
name: Cache Health
steps:
- name: Check Cache
uses: http
with:
method: GET
url: "{{vars.CACHE_URL}}/health"
test: res.code == 200Execution Timeline:
Time 0: Start database-check, api-check, cache-check simultaneously
Time T: All jobs complete (T = max execution time of all jobs)Dependent Job Execution
Jobs with dependencies wait for prerequisite jobs to complete:
name: Staged Deployment Validation
description: Validate deployment in dependency order
jobs:
- id: infrastructure
name: Infrastructure Check
steps:
- name: Database Connectivity
id: infrastructure
uses: http
with:
method: GET
url: "{{vars.DB_URL}}/ping"
test: res.code == 200
outputs:
db_healthy: res.code == 200
- id: services
name: Service Check
needs: [infrastructure]
steps:
- name: API Service
uses: http
with:
method: GET
url: "{{vars.API_URL}}/health"
test: res.code == 200
- id: integration
name: Integration Test
needs: [services]
steps:
- name: End-to-End Test
uses: http
with:
method: GET
url: "{{vars.API_URL}}/integration-test"
test: res.code == 200
- id: notification
name: Send Notification
needs: [integration]
steps:
- name: Notify Success
uses: hello
echo: "Deployment validation completed successfully"Execution Timeline:
Time 0: Start infrastructure job
Time T1: infrastructure completes → start services job
Time T2: services completes → start integration job
Time T3: integration completes → start notification job
Time T4: notification completes → workflow doneComplex Dependency Graphs
Jobs can have multiple dependencies and form complex execution graphs:
jobs:
# Foundation layer (parallel)
- id: database-setup
name: Database Setup
steps:
- name: Initialize Database
id: database-setup
outputs:
db_session_id: "{{random_str(16)}}"
- id: cache-setup
name: Cache Setup
steps:
- name: Initialize Cache
id: cache-setup
outputs:
cache_session_id: "{{random_str(16)}}"
# Service layer (depends on foundation)
- id: user-service
name: User Service Test
needs: [database-setup, cache-setup]
steps:
- name: Test User Service
id: user-service
outputs:
user_service_ready: true
- id: order-service
name: Order Service Test
needs: [database-setup] # Only needs database
steps:
- name: Test Order Service
id: order-service
outputs:
order_service_ready: true
# Integration layer (depends on services)
- id: integration-test
name: Integration Test
needs: [user-service, order-service]
steps:
- name: Test Service Integration
uses: hello
echo: "Testing integration between user and order services"
# Reporting layer (depends on everything)
- id: final-report
name: Final Report
needs: [integration-test]
steps:
- name: Generate Report
uses: hello
echo: |
Execution Report:
Database Setup: {{outputs['database-setup'] ? "✅" : "❌"}}
Cache Setup: {{outputs['cache-setup'] ? "✅" : "❌"}}
User Service: {{outputs['user-service'] ? "✅" : "❌"}}
Order Service: {{outputs['order-service'] ? "✅" : "❌"}}Execution Timeline:
Time 0: Start database-setup, cache-setup (parallel)
Time T1: Both foundation jobs complete → start user-service, order-service
Time T2: Both service jobs complete → start integration-test
Time T3: integration-test completes → start final-report
Time T4: final-report completes → workflow doneStep Execution Model
Sequential Step Execution
Within a job, steps execute sequentially in the order defined:
jobs:
- id: user-workflow
name: User Management Workflow
steps:
- name: Step 1 - Create User
id: create
uses: http
with:
url: "{{vars.API_URL}}/users"
method: POST
body: '{"name": "Test User", "email": "test@example.com"}'
test: res.code == 201
outputs:
user_id: res.body.user.id
- name: Step 2 - Verify User
id: verify
uses: http
with:
method: GET
url: "{{vars.API_URL}}/users/{{outputs.create.user_id}}"
test: res.code == 200
outputs:
user_verified: true
- name: Step 3 - Update User
id: update
uses: http
with:
url: "{{vars.API_URL}}/users/{{outputs.create.user_id}}"
method: PUT
body: '{"name": "Updated User"}'
test: res.code == 200
- name: Step 4 - Delete User
uses: http
with:
url: "{{vars.API_URL}}/users/{{outputs.create.user_id}}"
method: DELETE
test: res.code == 204
- name: Step 5 - Confirm Deletion
uses: http
with:
method: GET
url: "{{vars.API_URL}}/users/{{outputs.create.user_id}}"
test: res.code == 404Step Execution Order:
Step 1 → Step 2 → Step 3 → Step 4 → Step 5Each step waits for the previous step to complete before starting.
Conditional Step Execution
Steps still run in order; skipif only decides whether each one executes. The expression sees the same context as test, including the outputs of earlier steps.
steps:
- name: Primary Health Check
id: primary
uses: http
with:
method: GET
url: "{{vars.primary_url}}/health"
outputs:
primary_healthy: res.code == 200
- name: Backup Service Check
id: backup
uses: http
skipif: outputs.primary.primary_healthy
with:
method: GET
url: "{{vars.backup_url}}/health"
outputs:
backup_healthy: res.code == 200
- name: Report
uses: hello
echo: |
Primary: {{outputs.primary.primary_healthy ? "Online" : "Offline"}}
Backup: {{outputs.backup_healthy ?? "not checked"}}State Management
Job State Tracking
Probe tracks comprehensive state information for each job:
# Job states available for reference:
jobs:
- id: example-job
name: example-job
steps:
- name: Example Step
uses: hello
echo: "Job states can be referenced from other jobs"
- id: dependent-job
name: dependent-job
needs: [example-job]
steps:
- name: Check Job States
uses: hello
echo: |
Job State Information:
Step State and Output Management
Each step produces state and output information:
steps:
- name: API Test Step
id: api-test
uses: http
with:
method: GET
url: "{{vars.API_URL}}/test"
test: res.code == 200 && (rt.sec * 1000) < 1000
outputs:
response_time: (rt.sec * 1000)
status_code: res.status
api_healthy: res.code == 200
- name: Reference Previous Step
uses: hello
echo: |
Previous Step Information:
Step outputs:
Response time: {{outputs['api-test'].response_time}}ms
Status code: {{outputs['api-test'].status_code}}
API healthy: {{outputs['api-test'].api_healthy}}Cross-Job State References
Jobs can reference state from other jobs:
jobs:
- id: health-check
name: Health Check
steps:
- name: Check Service
id: health-check
outputs:
service_healthy: true
- id: performance-test
name: Performance Test
needs: [health-check]
steps:
- name: Load Test
id: performance-test
outputs:
avg_response_time: 250
- id: reporting
name: Generate Report
needs: [health-check, performance-test]
steps:
- name: Status Report
uses: hello
echo: |
System Status Report:
Performance Test: {{
"⏸️ Skipped"
}}
"Average Response Time: " + outputs['performance-test'].avg_response_time + "ms" :
"Performance data not available"}}Timing and Performance
Execution Timing
Probe tracks timing information at multiple levels:
jobs:
- id: timing-example
name: Timing Example
steps:
- name: Quick Operation
id: quick
uses: http
with:
method: GET
url: "{{vars.API_URL}}/ping"
test: res.code == 200
outputs:
ping_time: (rt.sec * 1000)
- name: Slow Operation
id: slow
uses: http
with:
method: GET
url: "{{vars.API_URL}}/complex-query"
test: res.code == 200
outputs:
query_time: (rt.sec * 1000)
- name: Timing Summary
uses: hello
echo: |
Operation Timing:
Quick operation: {{outputs.quick.ping_time}}ms
Slow operation: {{outputs.slow.query_time}}ms
Total step time: {{outputs.quick.ping_time + outputs.slow.query_time}}ms
Performance classification:
Quick: {{outputs.quick.ping_time < 100 ? "Excellent" : (outputs.quick.ping_time < 500 ? "Good" : "Slow")}}
Slow: {{outputs.slow.query_time < 1000 ? "Fast" : (outputs.slow.query_time < 5000 ? "Acceptable" : "Too Slow")}}Timeout Management
Configure timeouts at different levels:
jobs:
- id: timeout-management
name: Timeout Management Example
timeout: 300s # Job-level timeout (5 minutes)
steps:
- name: Quick API Call
uses: http
timeout: 10s # Step-level timeout
with:
method: GET
url: "{{vars.API_URL}}/quick"
test: res.code == 200
- name: Database Query
uses: http
timeout: 60s # Longer timeout for complex operation
with:
method: GET
url: "{{vars.DB_API}}/complex-query"
test: res.code == 200
- name: External Service Call
uses: http
timeout: 30s # External services may be slower
with:
method: GET
url: "{{vars.EXTERNAL_API}}/data"
test: res.code == 200Parallel Execution Optimization
Optimize workflow execution through effective parallelization:
name: Optimized Parallel Execution
description: Efficiently organize jobs for maximum parallelism
jobs:
# Tier 1: Independent foundation checks (all parallel)
- id: database-check
name: Database Health
steps:
- name: DB Connection Test
uses: http
with:
method: GET
url: "{{vars.DB_URL}}/ping"
test: res.code == 200
- id: cache-check
name: Cache Health
steps:
- name: Cache Connection Test
uses: http
with:
method: GET
url: "{{vars.CACHE_URL}}/ping"
test: res.code == 200
- id: network-check
name: Network Connectivity
steps:
- name: External API Test
uses: http
with:
method: GET
url: "{{vars.EXTERNAL_API}}/ping"
test: res.code == 200
# Tier 2: Service-level checks (parallel, depend on infrastructure)
- id: user-service-test
name: User Service Test
needs: [database-check, cache-check]
steps:
- name: User API Test
uses: http
with:
method: GET
url: "{{vars.USER_API}}/health"
test: res.code == 200
- id: order-service-test
name: Order Service Test
needs: [database-check]
steps:
- name: Order API Test
uses: http
with:
method: GET
url: "{{vars.ORDER_API}}/health"
test: res.code == 200
- id: notification-service-test
name: Notification Service Test
needs: [network-check]
steps:
- name: Notification API Test
uses: http
with:
method: GET
url: "{{vars.NOTIFICATION_API}}/health"
test: res.code == 200
# Tier 3: Integration tests (depend on services)
- id: user-order-integration
name: User-Order Integration
needs: [user-service-test, order-service-test]
steps:
- name: Integration Test
uses: http
with:
method: GET
url: "{{vars.API_URL}}/integration/user-order"
test: res.code == 200
# Tier 4: Final validation (depends on integration)
- id: end-to-end-test
name: End-to-End Test
needs: [user-order-integration, notification-service-test]
steps:
- name: Complete Workflow Test
uses: http
with:
method: GET
url: "{{vars.API_URL}}/e2e/complete-workflow"
test: res.code == 200Execution Visualization:
Time 0-T1: database-check, cache-check, network-check (parallel)
Time T1-T2: user-service-test, order-service-test, notification-service-test (parallel)
Time T2-T3: user-order-integration
Time T3-T4: end-to-end-testError Propagation and Recovery
Error Propagation Model
Understanding how errors propagate through the execution model:
jobs:
- id: critical-foundation
name: Critical Foundation
steps:
- name: Critical Check
uses: http
with:
method: GET
url: "{{vars.CRITICAL_SERVICE}}/health"
test: res.code == 200
# A failing step marks the job failed; later steps of the job still run
- id: dependent-service
name: Dependent Service
needs: [critical-foundation] # Won't execute if foundation fails
steps:
- name: Service Test
uses: http
with:
method: GET
url: "{{vars.SERVICE_URL}}/test"
test: res.code == 200
- id: resilient-check
name: Resilient Check
# No dependencies - always executes
steps:
- name: Independent Check
uses: http
with:
method: GET
url: "{{vars.INDEPENDENT_SERVICE}}/health"
test: res.code == 200
- id: conditional-cleanup
name: Conditional Cleanup
needs: [critical-foundation, dependent-service, resilient-check]
steps:
- name: Cleanup Failed State
uses: hello
echo: |
Cleaning up after failures:Recovery Execution Model
Implement recovery workflows that execute based on failure patterns:
jobs:
- id: primary-workflow
name: Primary Workflow
steps:
- name: Main Process
id: main
uses: http
with:
method: GET
url: "{{vars.API_URL}}/main-process"
test: res.code == 200
outputs:
process_successful: res.code == 200
- id: recovery-workflow
name: Recovery Workflow
steps:
- name: Diagnose Failure
id: diagnose
uses: http
with:
method: GET
url: "{{vars.API_URL}}/diagnostics"
test: res.code == 200
outputs:
diagnosis: res.body.issue_type
- name: Automated Recovery
uses: http
with:
url: "{{vars.API_URL}}/recovery/auto"
method: POST
test: res.code == 200
- name: Manual Recovery Alert
uses: hello
echo: "🚨 Critical failure detected - manual intervention required"
- id: validation-workflow
name: Validation Workflow
needs: [primary-workflow, recovery-workflow]
steps:
- name: Validate Final State
id: validation-workflow
uses: http
with:
method: GET
url: "{{vars.API_URL}}/validate"
test: res.code == 200
outputs:
system_healthy: res.code == 200
- id: final-report
name: Final Report
needs: [validation-workflow]
steps:
- name: Execution Summary
uses: hello
echo: |
Workflow Execution Summary:
Overall result: {{
"❌ System failed"
}}Resource Management
Plugin Lifecycle Management
Probe manages action plugins throughout workflow execution:
jobs:
- id: plugin-intensive-workflow
name: Plugin Intensive Workflow
steps:
# HTTP plugin loaded for this step
- name: API Test
uses: http
with:
method: GET
url: "{{vars.API_URL}}/test"
test: res.code == 200
# SMTP plugin loaded for this step
- name: Send Notification
uses: smtp
with:
addr: "{{vars.SMTP_HOST}}:25"
from: "probe@example.com"
to: "admin@company.com"
subject: "Test Completed"
session: 1
message: 1
length: 500
echo: "API test completed successfully"
- name: Debug Message
uses: hello
with:
message: "Debug checkpoint reached"
# HTTP plugin reused (already loaded)
- name: Follow-up API Test
uses: http
with:
method: GET
url: "{{vars.API_URL}}/follow-up"
test: res.code == 200Plugin lifecycle:
- Plugin loaded when first action is encountered
- Plugin reused for subsequent actions of same type
- Plugin cleaned up after job completion
Memory and Performance Optimization
Probe optimizes execution for performance and resource usage:
jobs:
- id: optimized-workflow
name: Performance Optimized Workflow
steps:
# Efficient: Direct property access
- name: User Data Collection
id: user-data
uses: http
with:
method: GET
url: "{{vars.API_URL}}/users"
test: res.code == 200
outputs:
user_count: res.body.total_users # Extract specific value
first_user_id: res.body.users[0].id # Direct array access
# Avoid: large_user_list: res.body.users (stores entire array)
# Efficient: Conditional processing
- name: Process Large Dataset
uses: http
with:
method: GET
url: "{{vars.API_URL}}/users/batch-process"
test: res.code == 200
# Efficient: Scoped outputs
- name: Summary Generation
uses: hello
echo: |
Processing Summary:
Total users: {{outputs['user-data'].user_count}}
First user: {{outputs['user-data'].first_user_id}}
# Efficient output without storing unnecessary dataBest Practices
1. Dependency Design
# Good: Logical dependency grouping
jobs:
- id: infrastructure
name: infrastructure
- id: application
name: application
needs: [infrastructure]
- id: integration
name: integration
needs: [application]
# Avoid: Unnecessary dependencies
jobs:
- id: independent-check-1
name: independent-check-1
- id: independent-check-2
name: independent-check-2
needs: [independent-check-1] # Unnecessary if truly independent2. Error Handling Strategy
# Good: Strategic error handling
- name: Critical Operation
test: res.code == 200
- name: Optional Operation
test: res.code == 2003. Output Efficiency
# Good: Efficient outputs
outputs:
essential_data: res.body.id
computed_value: len(res.body.items)
status_flag: res.code == 200
# Avoid: Storing large objects
outputs:
# entire_response: res.body # Could be very large4. Execution Flow Documentation
name: Well-Documented Workflow
description: |
Execution flow:
1. Infrastructure validation (parallel)
2. Service health checks (parallel, depends on infrastructure)
3. Integration testing (sequential, depends on services)
4. Reporting (depends on all previous stages)
Expected execution time: 2-5 minutes
Critical path: infrastructure → services → integration → reportingWhat’s Next?
Now that you understand the execution model, explore:
- File Merging - Learn configuration composition techniques
- How-tos - See practical execution patterns in action
- Reference - Detailed syntax and configuration reference
Understanding the execution model helps you design efficient, predictable workflows that make optimal use of parallelism and handle failures gracefully.