Skip to main content
Plexara
Product9 min read

609 - One run per event: an automation that answers each return

An automation that names a webhook source runs once for each event the source stores, seconds after it is stored, and reads that event as its input. Built on the demo tenant: a returns source, an automation that checks each return against the store’s day and posts the ones worth a look, and the run history that records each run with its trigger, source, and event id. What happens on a burst, the order runs keep when they go one at a time, why a failed event run alerts its owner and is never replayed while the next event still runs, and when a scheduled batch is the better shape.

What you will take away from this lesson

Lesson 607 ended with a scheduled automation that reads a feed in batches and keeps its place. Some work does not want a batch. A return over a few hundred dollars, a supplier application, a delay notice from a carrier: each one is a decision somebody should see while it is still fresh, and waiting for the next fire to sweep it up adds delay for nothing.

This lesson builds the other shape on the demo tenant: an automation that names a source and runs once for each return it stores, checks it against the store’s day, and posts the ones a person should look at.

Learning Objectives

  1. 01Make an automation run once for each event a source stores, and read the event it was made for.
  2. 02Read a per-event run in the run history: the trigger, the source, and the event id it was made once for.
  3. 03Explain what happens on a burst: a bounded queue, the backlog recorded as skipped, and arrival order when runs go one at a time.
  4. 04State what a failed event run does: it alerts its owner, it is never run again on its own, and the next event still runs.
  5. 05Choose between one run per event and a scheduled batch.

Where this lesson sits

Lesson 604 named the four triggers and 607 showed where events come from. This lesson takes the third trigger, once per event, and builds an automation around it. The channel it posts to is the one from 608.

600 Series: Automations

Open index

The 600 series picks up the registered table from the 500 series and the prompt mechanics from the 400 series.

What runs on the demo tenant

Four parts, three of them already familiar from 607 and 608. The new one is a source that carries only returns, so that only a return starts a run.

The return check on the demo tenant

The source

pos-returns

A webhook source an administrator created beside pos-sales. Each request carries a batch of returns, split into one event per return, with the sale id as the event id and the store id as the key. Its sender connection names its token as a stored secret.

The sender

ACME POS sales simulator

The same simulator 607 follows. After it posts each batch of sales to pos-sales, it posts the batch’s returns, about one in twenty-five sales, to pos-returns.

The automation

acme-pos-return-event-609

Names pos-returns as its event source and runs one at a time, queued. Each run counts the store’s returns in the 24 hours up to this one and flags the return when it is $250 or more, or the store’s third in a day.

Where a flag goes

ops-events

The JSON webhook channel from 608, signed by its connection and received by the ops-events source, so a system can route on data.type and a person can read the body.

The script

An event run is an ordinary run with one more input. The agent wrote this script through the same loop as every other in the series, with tests that build the event a run will read.

acme-pos-return-event-609, its main() and handle()

def handle(event):
    ret = event["payload"]
    if ret.get("event") != "sale.returned":
        return {"sale_id": ret.get("sale_id"), "flagged": False, "skipped": "not a return"}
    day = store_day(ret, event["received_at"])
    reasons = reasons_for(ret, day)
    if reasons:
        platform.notify(
            channel = "ops-events",
            title = "Return to review at %s" % ret["store_name"],
            body = "Sale %s was returned for $%s (%s). Flagged for %s." % (...),
            key = "returns-watch:%s" % ret["store_id"],
            data = {"type": "pos.return.flagged", "sale_id": ..., "reasons": reasons},
        )
    return {"sale_id": ..., "store": ..., "amount": ..., "returns_24h": ...,
            "flagged": len(reasons) > 0, "reasons": reasons}

def main():
    if run.event == None:
        platform.result({"flagged": False, "skipped": "started without an event"})
        return
    platform.result(handle(run.event))

# store_day counts the store's returns stored in the 24 hours before this
# one, excluding this event id, and adds this one.

The run reads its event as run.event: the event id, its type and key as the source read them, when it was received, the source’s name, and the payload. On every other kind of run run.event is None, so the script says so instead of failing when somebody presses Run.

The tests build events the way the source stores them and call handle() directly: a large return, a store’s third return, an ordinary return that posts nothing, an empty count, an event that is not a return, and a run with no event. The save reached all but one statement. Each post carries a key per store, so a store that keeps returning inside the channel’s repeat window is counted, not posted again.

Naming the source

What turns the script into a per-event automation is one setting. It was set with two others in the same call: the source, and one run at a time, queued, so a burst is answered in the order it arrived.

Naming the source

What the agent calls. This is the exchange the agent has with Plexara on your behalf, shown for the technical reader. You ask in plain language; you never type any of it.

manage_script update
  name          acme-pos-return-event-609
  event_source  pos-returns
  exclusive     queue

-> status   updated
   message  Saved, and this version is what runs now: it presents the roles
            you held when you saved it, and any schedule fires it.

The same setting is on the automation’s page under Events, beside the schedule, and clearing it stops the runs. Naming a source means reading every event it stores, so an administrator may name any source and an owner only the source of a form they own. A script with a required parameter and no default cannot run from an event, because an event passes no parameters.

The runs it made

From then on each return the simulator posted made one run, without anyone asking for it and without a schedule.

The run history, one row per return

What the agent calls. This is the exchange the agent has with Plexara on your behalf, shown for the technical reader. You ask in plain language; you never type any of it.

manage_script runs  acme-pos-return-event-609

   trigger  event_source  event_id                         status     duration
   event    pos-returns   dpx_73252d4f…-0008               succeeded  279 ms
   event    pos-returns   dpx_73252d4f…-0020               succeeded  203 ms
   event    pos-returns   dpx_733c9b2c…-0038               succeeded  201 ms
   event    pos-returns   dpx_733c9b2c…-0048               succeeded  213 ms
   event    pos-returns   dpx_109c51c1…-0005               succeeded  186 ms

manage_script get_run  dpx_475722ec…

-> trigger       event
   event_source  pos-returns
   result        {"sale_id": "dpx_5396c560…-0002", "store": "ACME Store #165",
                  "amount": 178.34, "flagged": false, "reasons": []}

Three simulator deliveries a few seconds apart carried five returns, and the history shows five runs, each naming the source and the event it was made once for. Each run started within a second of its delivery and took about a fifth of a second, almost all of it the count query. The first return checked was $178.34 and the store’s first of the day, so the run recorded it and posted nothing.

What one run per event guarantees

Six rules decide what happens when events arrive faster than runs finish, and when a run fails.

What one run per event guarantees

  1. 01

    One event, one run

    A run is made once for each event id. A delivery the sender retries, or two servers noticing the same event, makes no second run.

  2. 02

    The sender never waits for the automation

    The run is queued in the same step that stores the event, before the sender is answered, and executes afterwards on the platform’s workers. A slow or failing automation cannot slow the sender down.

  3. 03

    A burst is bounded

    An automation holds at most 100 event runs waiting. An event past that is recorded as a skipped run whose reason says the queue was full and the event is still in its source, and the Events section counts the week’s skips.

  4. 04

    One at a time keeps arrival order

    An automation set to run one at a time, refused or queued, runs its event runs in the order the events arrived. An event run is never refused for an open run; it waits.

  5. 05

    A failed event run alerts and is not replayed

    It is recorded with its cause like any run, and its owner is told as for a failed scheduled run. The platform does not run it again on its own, and the next event still runs.

  6. 06

    The event stays in its table

    Whatever the automation does, the event is in the source’s table, so a run that failed or was skipped can be made good later by a run by hand or a scheduled sweep that reads the table.

One run per event, or a scheduled batch

Both shapes read the same events. The choice is about what each event needs and how many of them there are.

One run per event, or a scheduled batch

QuestionOne run per eventA scheduled batch
Does each event need its own answer?Yes: a flag, a reply, a write to another system for this one record.No: a total, a file, or a dashboard over many events.
How soon does somebody act on it?Within seconds of the event being stored.At the next fire, which is fine for a daily or hourly summary.
How many events arrive?Tens or hundreds an hour, each worth a run.Thousands an hour, where one query over the window is cheaper than a run each.
What does a run need to remember?Nothing: the event is the input.Its place in the feed: a watermark in state, as in 607.

The demo’s point-of-sale feed carries about 8,600 events a day. Running an automation for every sale would make thousands of runs that do nothing, which is why the returns go to their own source and only they start a run. The scheduled readers in 607 keep reading the whole feed.

Where this goes next

Each return on the demo tenant is now checked within seconds of being stored, the ones worth a look reach the operations channel with their reasons, and every check is in the run history with the event it was made for.

The same shape serves a form: its submissions are a source, and an automation that names it runs once per submission, which the next lesson follows for a mailbox. What happens when one of these runs fails at an hour nobody is watching is 611.

Key terms

Four terms cover an automation that runs once per event.

Key Terms

Event source
The webhook, form, or mailbox source an automation names. Each event it stores queues one run.
run.event
What an event run reads: the event id, type, key, when it was received, the source, and the payload. None on every other run.
Event run
A run recorded with trigger event, the source, and the event id it was made once for.
Skipped as backlog
An event that arrived while the automation already held its limit of waiting runs. Recorded, never executed, and still in its source.