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

> Learn to repeat jobs at intervals in Probe. Easily automate workflows with the repeat block for scheduled tasks or load testing.

- Repository: [Tomohisa Oda/probe](https://github.com/linyows/probe)
- Tags: how-to-guide
- Published: 2026-03-06

---

**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`](https://github.com/linyows/probe/blob/main/repeat.go) (lines 99-104), the `Repeat` struct defines the configuration schema:

```go
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`](https://github.com/linyows/probe/blob/main/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`](https://github.com/linyows/probe/blob/main/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`](https://github.com/linyows/probe/blob/main/executor.go) implements two distinct execution strategies based on the `async` flag.

### Synchronous Mode (Default)

When `async: false` (the default), `executeJobRepeatLoop` in [`executor.go`](https://github.com/linyows/probe/blob/main/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`](https://github.com/linyows/probe/blob/main/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:

```yaml
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:

```yaml
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):

```yaml
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`](https://github.com/linyows/probe/blob/main/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`](https://github.com/linyows/probe/blob/main/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:

```bash
export PROBE_MAX_REPEAT_COUNT=50000
probe run my-workflow.yml

```

The validator in [`repeat.go`](https://github.com/linyows/probe/blob/main/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`](https://github.com/linyows/probe/blob/main/executor.go) lines 82-97
- **Asynchronous mode** spawns goroutines with a ticker for concurrent execution, implemented in [`executor.go`](https://github.com/linyows/probe/blob/main/executor.go) lines 101-145
- **Template variable** `{{repeat_index}}` provides access to the current iteration number via context fields in [`job.go`](https://github.com/linyows/probe/blob/main/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`](https://github.com/linyows/probe/blob/main/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`](https://github.com/linyows/probe/blob/main/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.