Python worker
The zeridion-flare package ships a built-in worker that runs your
background jobs end-to-end: it registers with Zeridion, long-polls for work,
dispatches each job to your handler, sends liveness heartbeats with progress,
honors server-side cancellation, reports the outcome, and drains in-flight jobs
on shutdown.
The worker is an opt-in import so thin-client usage stays lean:
from zeridion_flare.worker import FlareWorker, JobContext
Requires: Python 3.10+ · the same httpx dependency as the client (no extra
mandatory packages). Typed payloads via Pydantic are an
optional extra.
A complete worker
from dataclasses import dataclass
from zeridion_flare.worker import FlareWorker, JobContext
worker = FlareWorker() # reads FLARE_API_KEY from the environment
@dataclass
class NewUserEvent:
email: str
name: str
@worker.job("SendWelcomeEmail", queue="email", max_attempts=5, timeout=60)
def send_welcome(payload: NewUserEvent, ctx: JobContext) -> None:
# At-least-once delivery → make handlers idempotent.
send_email(payload.email)
ctx.report_progress(1.0)
@worker.recurring("NightlyCleanup", cron="0 3 * * *", queue="maintenance", timezone="UTC")
def nightly(ctx: JobContext) -> None:
purge_expired()
if __name__ == "__main__":
worker.run() # blocks; SIGTERM / SIGINT → graceful drain
Run it like any Python program:
export FLARE_API_KEY=zf_live_sk_...
python worker.py
How it runs
The worker is threaded: jobs run in a thread pool, and because Python releases the GIL on I/O, I/O-bound handlers get real concurrency. Each cycle:
- Register — on startup, the worker announces its id, queues, job types, and any recurring schedules. Registration is best-effort; if it fails the worker keeps polling.
- Poll — it long-polls for jobs, advertising how many free slots it has so the server never hands it more work than it can run.
- Dispatch — each returned job is decoded, its payload is bound to your handler's type, and the handler runs on a pool thread.
- Heartbeat — while a job runs, the worker sends periodic heartbeats carrying the latest reported progress. If the server responds asking to cancel, the job's cancellation handle is tripped.
- Acknowledge — when the handler returns, the worker reports success; if it raises (including on cancellation or timeout), it reports failure. The server decides whether to retry or dead-letter — the worker never makes that call itself.
- Drain — on
SIGTERM/SIGINTthe worker stops polling, waits for in-flight jobs to finish (up to the shutdown grace window), and exits.
Worker identity
Each worker gets a unique id of the form wrk_<host>_<pid>_<random>. The host
segment is sanitized to the allowed character set and the random suffix is
drawn from a cryptographic source, so two workers — even replicas on the same
hostname — never collide.
At-least-once & idempotency
The final acknowledgement is best-effort. If a worker finishes a job but
crashes before the ack lands, the server redelivers it. Write handlers to be
idempotent — for example, check whether the work for ctx.job_id was already
done before repeating a side effect.
Cancellation is cooperative
There is no safe way to forcibly kill a running Python thread, so cancellation
(on timeout, server cancel, or shutdown) is cooperative: your handler must
notice it. Check ctx.is_cancelled() in loops, sleep on the cancellation
handle instead of time.sleep, and pass it to long-running calls. A CPU-bound
handler that never yields cannot be interrupted and is bounded only by the
server's own reclaim window — see Job context.
Next
- Defining jobs — payload handlers, typed payloads, discovery.
- Recurring jobs — cron schedules and time zones.
- Job context — metadata, progress, cancellation.
- Configuration — concurrency, timeouts, drain window.
- Asyncio — the async worker (fast-follow).