Synchronoid is in private beta. New workspaces open in small batches every few weeks. Join the waitlist →

Pipelines and steps

A pipeline is an ordered group of steps that you run as one unit. Assets cover most needs. Pipelines add explicit control over order, branching and retries for work that does not fit into a single asset.

Steps

A step is the smallest unit of work in a run. It is a function decorated with @sy.step. Synchronoid records the start, end, inputs and outputs of every step, and it never runs a finished step twice for the same inputs.

pipelines/listings.py
import synchronoid as sy

@sy.step(retry=sy.Retry(max_attempts=3, backoff="exponential"))
def fetch_listings(ctx, region):
    return ctx.source.get("/listings", params={"region": region})

@sy.step
def normalize(ctx, payload):
    return [clean(item) for item in payload.records()]

Pipelines

A pipeline connects steps. Calling a step inside a pipeline does not run it immediately. It adds the step to the plan, and the return value is a reference to the future result.

pipelines/listings.py
@sy.pipeline(name="listings_sync")
def listings_sync(regions):
    for region in regions:
        payload = fetch_listings(region)
        rows = normalize(payload)
        publish(rows)

The three steps for each region form a chain, and the chains for different regions run in parallel. Because steps pass references, large payloads are never copied between workers.

Durable runs

A run is one execution of a pipeline or of one or more assets. Runs are durable: Synchronoid writes the result of each finished step before it starts the next. If a worker crashes, restarts or is replaced during a deploy, the run continues on another worker from the last finished step.

What happenedWhat Synchronoid does
Worker dies in the middle of a stepThe step is run again on another worker. It is idempotent, so partial output is discarded.
Worker dies between stepsThe next step starts. Finished steps are not repeated.
A step raises an errorThe step's retry policy applies. After the last attempt the run is marked failed.
You deploy new codeRunning runs finish on the revision they started with.

Why steps must be idempotent

Because a step can run more than once, running it twice has to be safe. Synchronoid helps in two ways. Writes to storage are keyed by run id, step name and record key, so a second write replaces the first. Inputs are immutable snapshots, so a step that is run again sees the same data.

External side effects

Calls to systems outside Synchronoid, such as posting to a chat channel, are not protected. Use ctx.run_id as an idempotency key, or move the side effect into a sensor or an alert rule.

Retries

Set a retry policy on a step, an asset, or a whole source. The most specific setting wins.

pipelines/listings.py
retry = sy.Retry(
    max_attempts=5,
    backoff="exponential",    # 2s, 4s, 8s, 16s ...
    retry_on=[sy.errors.Timeout, sy.errors.HttpStatus(429, 503)],
)

When a source keeps failing across many runs, Synchronoid pauses it automatically and alerts an operator, so a broken provider does not use up your quota. See Retries and replays.

Replaying a run

You can replay any run from the control room or the CLI. A replay re-executes the steps using the stored inputs, so it does not call the source again.

terminal
$ syn runs list --status failed
$ syn runs replay run_8f2c1a

Choosing between assets and pipelines