How to Repeat Jobs at Intervals in Probe: A Complete Guide to Workflow Automation

Probe supports repeating jobs at configurable intervals using the repeat block in workflow YAML, allowing you to run steps multiple times with delays between executions or in parallel for load testing.

The linyows/probe repository provides a flexible job repetition system built around three core components: the Repeat struct for configuration, the Executor for orchestration, and the Job definition for context propagation. This guide explains how to configure interval-based repetition, choose between synchronous and asynchronous execution modes, and leverage template variables for dynamic workflows.

Understanding the Repeat Block Structure

The Repeat Struct and Validation

In repeat.go (lines 99-104), the Repeat struct defines the configuration schema:

type Repeat struct {
    Count    int
    Interval Interval
    Async    bool
}

The system validates the count field against PROBE_MAX_REPEAT_COUNT, which defaults to 10,000. If you attempt to configure a count exceeding this limit, the validator in repeat.go (lines 106-113) returns an error indicating the environment variable name for adjustment.

Interval Parsing and Formats

The Interval type in repeat.go (lines 42-86) accepts multiple formats:

  • Integer values: Interpreted as seconds (e.g., 30 equals 30 seconds)
  • Duration strings: Standard Go duration formats like 30s, 1m, 2m30s

During YAML unmarshaling, the UnmarshalYAML method automatically detects the format and converts it to a time.Duration for internal use.

Synchronous vs Asynchronous Execution

The Executor in executor.go implements two distinct execution strategies based on the async flag.

Synchronous Mode (Default)

When async: false (the default), executeJobRepeatLoop in executor.go (lines 82-97) runs jobs sequentially:

  1. Executes Job.Start for the current iteration
  2. Checks JobScheduler.ShouldRepeatJob(jobID) to determine continuation
  3. Calls sleepBetweenRepeats (lines 48-55) to execute time.Sleep(e.job.Repeat.Interval.Duration) between iterations

This mode ensures that each repetition completes before the next begins, making it ideal for health checks or sequential monitoring tasks.

Asynchronous Mode for Concurrent Testing

When async: true, executeJobRepeatLoopAsync in executor.go (lines 101-145) enables parallel execution:

  • Creates a time.NewTicker using the configured interval
  • Spawns a goroutine for each iteration immediately
  • Uses the ticker to space out the goroutine launches
  • Tracks overall success using atomic.Bool

This mode is specifically designed for load testing scenarios where you need overlapping job executions to simulate concurrent user traffic or stress test services.

Practical Configuration Examples

Basic Health Check with Intervals

Configure a job to check service health three times with five-second pauses:

jobs:
  health-check:
    name: "Check service health"
    repeat:
      count: 3
      interval: 5s
    steps:
      - name: "curl health endpoint"
        run: curl -f http://localhost/health

The job executes sequentially, waiting five seconds between each curl command.

Load Testing with Async Repetition

Simulate concurrent load by running four overlapping instances with two-second spacing:

jobs:
  stress-test:
    name: "Load test"
    repeat:
      count: 4
      interval: 2s
      async: true
    steps:
      - name: "Run load test"
        run: ./load-test --duration=30s

Four goroutines execute simultaneously, each starting two seconds after the previous, creating overlapping load test windows.

Accessing the Repeat Index in Steps

Use the {{repeat_index}} template variable to access the current iteration number (1-based):

jobs:
  numbered:
    repeat:
      count: 3
      interval: 1s
    steps:
      - name: "Echo iteration"
        run: echo "Processing iteration {{repeat_index}}"

Output:


Processing iteration 1
Processing iteration 2
Processing iteration 3

The Job struct in job.go (lines 14-15) provides context fields IsRepeating, RepeatCurrent, and RepeatTotal that enable this template resolution.

Configuration Limits and Environment Variables

Probe enforces safety limits through environment variables defined in repeat.go:

Variable Default Purpose
PROBE_MAX_REPEAT_COUNT 10000 Maximum allowed value for repeat.count
PROBE_MAX_ATTEMPTS 10000 Maximum retry attempts for step-level retries

To run a job more than 10,000 times, override the limit before execution:

export PROBE_MAX_REPEAT_COUNT=50000
probe run my-workflow.yml

The validator in repeat.go (lines 106-113) references the environment variable name in error messages when limits are exceeded.

Summary

  • Repeat configuration uses the repeat block with count, interval, and optional async fields in workflow YAML
  • Synchronous mode (default) runs jobs sequentially with time.Sleep between iterations, implemented in executor.go lines 82-97
  • Asynchronous mode spawns goroutines with a ticker for concurrent execution, implemented in executor.go lines 101-145
  • Template variable {{repeat_index}} provides access to the current iteration number via context fields in job.go
  • Safety limits default to 10,000 repetitions, configurable via PROBE_MAX_REPEAT_COUNT environment variable

Frequently Asked Questions

What is the maximum number of times a job can repeat in Probe?

By default, Probe limits job repetition to 10,000 executions. This limit is enforced by the validator in repeat.go and can be increased by setting the PROBE_MAX_REPEAT_COUNT environment variable to a higher value before running the workflow.

How does Probe handle the interval between repeated job executions?

Probe supports two execution modes. In synchronous mode (the default), the executor uses time.Sleep with the specified duration between sequential runs. In asynchronous mode, the executor creates a time.NewTicker to space out goroutine launches, allowing jobs to overlap while maintaining the interval between starts.

Can I access the current repetition number within job steps?

Yes. Probe exposes the current iteration through the {{repeat_index}} template variable, which resolves to a 1-based index. This functionality is powered by context fields (IsRepeating, RepeatCurrent, RepeatTotal) defined in the Job struct in job.go, allowing steps to vary behavior based on the repetition count.

When should I use asynchronous repetition instead of synchronous repetition?

Use asynchronous repetition (async: true) when you need concurrent job execution for load testing or stress testing scenarios where overlapping runs simulate real-world traffic patterns. Use synchronous repetition for sequential monitoring, health checks, or any scenario where you need the previous execution to complete before the next begins.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →