Skip to Content
GuideConceptsExecution Model

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:

  1. Workflow Parsing: Parse and validate YAML configuration
  2. Dependency Resolution: Build execution graph based on job dependencies
  3. Job Scheduling: Schedule jobs for execution based on dependencies
  4. Step Execution: Execute steps sequentially within each job
  5. State Management: Track execution state and results
  6. 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 == 200

Execution 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 done

Complex 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 done

Step 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 == 404

Step Execution Order:

Step 1 → Step 2 → Step 3 → Step 4 → Step 5

Each 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 == 200

Parallel 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 == 200

Execution 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-test

Error 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 == 200

Plugin lifecycle:

  1. Plugin loaded when first action is encountered
  2. Plugin reused for subsequent actions of same type
  3. 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 data

Best 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 independent

2. Error Handling Strategy

# Good: Strategic error handling - name: Critical Operation test: res.code == 200 - name: Optional Operation test: res.code == 200

3. 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 large

4. 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 → reporting

What’s Next?

Now that you understand the execution model, explore:

  1. File Merging - Learn configuration composition techniques
  2. How-tos - See practical execution patterns in action
  3. 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.

Updated at