Skip to main content
Cloacina Documentation
Toggle Dark/Light/Auto mode Toggle Dark/Light/Auto mode Toggle Dark/Light/Auto mode Back to homepage

Trigger

Trigger

A Trigger starts one or more Workflows without a manual call. It is a named fan-out point: it has a firing rule and a primary workflow (on), and any number of workflows — across packages — can subscribe to its name. Two kinds of firing rule:

  • Poll — a function the scheduler runs on an interval; it decides whether to fire (and with what context).
  • Cron — fires on a cron schedule (expression + timezone).

Mental model

  • A trigger is registered with the runner’s scheduler, which polls it on its interval and fires when the rule says so.
  • Firing is deduplicated (a context hash) so the same fire doesn’t double-run.
  • Fan-out (CLOACI-T-0777 / T-0778). When a trigger fires, every workflow subscribed to it runs, not just the primary on workflow. A workflow subscribes by naming the trigger in #[workflow(triggers = ["my_trigger"])], and subscribers may live in other packages; they’re resolved from registry workflow metadata. The primary workflow drives the audit record, return value, and error propagation; secondary subscribers are best-effort (a secondary failure is logged, never fails the primary). A plain cron schedule (no trigger name) still binds exactly one workflow. The scheduler’s auto-poll and a manual fire (POST /v1/tenants/{t}/triggers/{name}/fire, server mode) fan out the same way.

Interfaces

Both kinds are authored with #[trigger] and bind to a workflow via on:

// poll trigger
#[trigger(on = "my_workflow", poll_interval = "30s")]
async fn my_poll(/* ... */) -> TriggerResult { /* Fire(ctx) | Skip */ }

// cron trigger
#[trigger(on = "my_workflow", cron = "0 9 * * *", timezone = "UTC")]
struct DailyTrigger;

Python exposes poll triggers as a decorator:

import cloaca

@cloaca.trigger(name="my_poll", poll_interval="30s")
def my_poll(context):
    return context   # return to fire; raise/None semantics per the API
Parity gap
There is no packaged/decorator cron trigger in Python — #[trigger(cron=…)] is Rust-only. Python does full cron at the runner API instead (register_cron_workflow, …); see Cron schedule. Tracked in [CLOACI-T-0688].
Not the same as reactor subscription fan-out
Trigger-name fan-out (one trigger name, many subscribing workflows) is a different primitive from the DB-backed reactor → workflow subscription fan-out. The latter has a reactor publisher, a durable reactor_firings log, and at-least-once delivery; this has none of those. See Trigger-name fan-out vs reactor subscription fan-out for the side-by-side.

See also