Iteration and Parallelism
for_each iterates a list. max is required, and declares the bound.
- attachments: for_each: "{{ message.attachments }}" as: file max: 25 concurrency: 4 on_error: continue # continue | fail_fast steps: - scan: { av.scan: { url: "{{ file.url }}" } } - store: when: "{{ scan.clean }}" s3.put: { key: "{{ message.id }}/{{ file.name }}" }The construct binds its id to a list of per-iteration scopes, indexed by position: {{ attachments[0].scan.clean }} and {{ attachments | length }}.
Each iteration creates a scope. No binding crosses from one iteration to the next. The loop binding that as names is visible inside the construct only.
Iteration order follows the source list. concurrency affects scheduling only, and MUST NOT affect results.
A source list longer than max fails the step before any iteration runs. Implementations MUST NOT truncate a source list.
max accepts a positive integer or the word unbounded. unbounded declares that the source list’s own length is the bound. An omitted max is a validation error: the author states the bound in every case. The source list is finite when the step starts, so the loop terminates under either value. unbounded-iteration is a capability, so a deployment can decline to execute an unbounded loop.
Failure within an iteration. on_error declares what a failed step inside one iteration does to the construct. It takes one of two values.
| Value | Behavior |
|---|---|
fail_fast |
The failure fails the construct. Iterations that have not started do not start, and iterations in progress are cancelled. Default. |
continue |
The failing iteration stops at its failed step. Other iterations run to completion, and the construct succeeds. |
A step of an iteration that did not run is recorded with the outcome not_run, and a reference to it resolves to null.
Every iteration scope holds a reserved error binding.
| Path | Type | Meaning |
|---|---|---|
error |
object? |
null when the iteration completed. Otherwise an object. |
error.step |
string |
The id of the step that failed. |
error.code |
string |
The error code, from the manifest’s error table or from the fault vocabulary. |
error.message |
string |
A human-readable description. Its text is implementation-defined. |
Failed iterations are therefore selected with the ordinary filters: {{ attachments | keep(current.error != null) }}.
parallel
Section titled “parallel”parallel runs named branches and joins them implicitly.
- enrich: parallel: crm: [{ lookup: { crm.find_contact: { email: "{{ message.from }}" } } }] billing: [{ lookup: { billing.account: { email: "{{ message.from }}" } } }] timeout: 30s on_error: continue # continue | fail_fastparallel binds its id to a scope whose members are the branch scopes, named by branch: {{ enrich.crm.lookup.deal_size }}. Branch names MUST match ^[a-z][a-z0-9_]*$ and MUST be unique. A branch MUST NOT reference another branch’s bindings.
Branch scheduling affects the order of external effects only, and MUST NOT affect results. The order in which branches contact external systems is unspecified.
on_error takes the values and the meanings that for_each defines. Each branch scope holds the same reserved error binding.