Skip to main content

Engine-agnostic dynamic scheduler for the z4j stack (Apache 2.0)

Project description

z4j-scheduler

PyPI version Python License

License: Apache 2.0

The modern Python scheduler in the z4j stack. Engine-agnostic, dynamic-CRUD, HA-ready. Replaces celery-beat, rq-scheduler, and APScheduler with a single tool that fires scheduled tasks at any Python task queue (Celery, RQ, Dramatiq, arq, taskiq, huey) via the existing z4j agent network.

z4j-scheduler is not standalone. It requires z4j-brain and is structurally part of the z4j stack.

Resource Link
Project home z4j.com
Full documentation z4j.dev/schedulers/z4j-scheduler/
Source github.com/z4jdev/z4j-scheduler
Issues github.com/z4jdev/z4j-scheduler/issues

Why migrate off celery-beat

In every conversation with operators running Python task infrastructure in production, the same six wishes come up:

  1. Change a schedule without a restart, propagate within a second.
  2. Run more than one instance for HA without duplicate fires.
  3. Manage schedules from a real UI, not Django admin.
  4. See an audit trail of who changed what schedule when.
  5. Schedule into multiple engines without wiring three separate scheduler tools.
  6. Get alerted when a schedule fails to fire or fires but the task fails.

z4j-scheduler delivers all six.

Concern celery-beat django-celery-beat z4j-scheduler
Engine support celery only celery only 6 engines (Celery, RQ, Dramatiq, Huey, arq, taskiq)
Dynamic CRUD restart needed 5 to 30s poll under 100ms via LISTEN/NOTIFY
HA / leader election no no yes (Postgres advisory lock)
Audit trail no no HMAC chain via z4j-brain
Sub-second propagation n/a no yes
10k+ schedules degrades degrades yes (1M tested, 495 MB RSS)
DST-correct partial partial yes (croniter + zoneinfo)
Catch-up policy per schedule no no yes (skip / fire_one / fire_all)
Schedule kinds cron, interval, clocked same cron, interval, one_shot, solar
Real CRUD UI no django-admin yes (z4j-brain dashboard)
Migration tools n/a n/a yes (4 importers + reverse export + 24h shadow comparator)
Native notifications no no yes (in-app, email, Slack, Telegram, webhook)

The trade-off: z4j-scheduler adds about 10 to 30ms per fire vs celery-beat's direct broker write because it goes through one extra hop (scheduler to brain to agent to engine). For minute-resolution and coarser schedules this is invisible. For sub-second workloads, use the engine's native scheduler.


Measured numbers (v1.1)

Numbers below are from a single Windows laptop with WSL-2-backed Docker; production hardware typically beats these.

Against the v1 GA targets

Metric Target Measured Margin
Memory at idle < 80 MB 39.7 MB 2.0x headroom
Memory at 10k schedules < 300 MB 52.6 MB (about 634 B / schedule) 5.7x headroom
Memory at 100k schedules (n/a, beyond target) 93.6 MB well in budget
Memory at 1M schedules (n/a) 495.6 MB fits in a 1 GB container
Startup time < 2 s 102.7 ms 19.5x headroom
Tick accuracy p50 +/- 100 ms 5.4 ms 18x headroom
Tick accuracy p99 +/- 500 ms 160.2 ms 3.1x headroom
Fires per second 100/s sustained 120/s met
HA failover time < 10 s 130 ms 76x headroom

Head-to-head vs celery-beat

Same workload (5 typical cron expressions and scaling tests) measured against celery.schedules.crontab running in-process.

Workload z4j-scheduler celery-beat Verdict
Single next-fire compute (p50, every-minute) 41 us 11 us celery 3.6x faster
Single next-fire compute (p99, every-5-min) 147 us 18 us celery 8x faster
Per-tick due-list scan at 100 schedules 1.4 ms 2.1 ms z4j 1.5x faster
Per-tick due-list scan at 10k schedules 131 ms 200 ms z4j 1.5x faster
RSS at 10k schedules in memory 6 MB (634 B/sched) 60 MB (6377 B/sched) z4j 10x lighter

Honest mixed result: celery's hand-tuned crontab class beats croniter on the per-call next-fire computation, but z4j-scheduler's per-tick and memory profile is strictly better at operational scale. Replacing celery-beat trades about 30 us of per-fire compute for about 10x less RAM and about 33% faster ticks.


Install

pip install z4j-scheduler
# or via the umbrella with extras:
pip install z4j[scheduler]

Run

z4j-scheduler serve \
  --brain-grpc-url brain:7701 \
  --brain-rest-url http://brain:7700 \
  --tls-cert /certs/scheduler.crt \
  --tls-key /certs/scheduler.key \
  --tls-ca /certs/brain-ca.crt

Full configuration reference (every env var, every flag) lives at z4j.dev/schedulers/z4j-scheduler/.

For the single-container homelab deploy, set Z4J_EMBEDDED_SCHEDULER=true on the brain image and skip running a separate scheduler process. Brain spawns z4j-scheduler as a supervised subprocess with auto-minted loopback mTLS PKI.

For Kubernetes, see the deployment guide at z4j.dev/schedulers/z4j-scheduler/.

Requirements

  • z4j-brain v1.1+ with the SchedulerService gRPC endpoint enabled
  • Postgres 17+ (shared with brain), required only for HA via advisory locks; single-instance deployments work without it
  • Python 3.13+

Migration from celery-beat

The full operator playbook lives at z4j.dev/schedulers/z4j-scheduler/. Quick version:

# 1. Read-only diff: see what would be imported.
z4j-scheduler import \
    --from django-celery-beat \
    --django-settings myapp.settings \
    --project acme-prod \
    --verify

# 2. Shadow-mode fire prediction over the next 24 hours.
#    Reports per-fire divergence in timing, args, kwargs, queue.
#    Verdict line says "Safe to flip" iff zero divergences.
z4j-scheduler import \
    --from django-celery-beat \
    --django-settings myapp.settings \
    --project acme-prod \
    --verify --duration 24h

# 3. Apply.
z4j-scheduler import \
    --from django-celery-beat \
    --django-settings myapp.settings \
    --project acme-prod

# 4. Stop celery beat. Start z4j-scheduler. Done.

Reverse migration (back-out plan)

Adoption is reversible. The export tool generates a Python module you paste into your Django settings to revert:

z4j-scheduler export --to celery --project acme-prod \
    --output beat_schedule.py

Targets: celery, rq, apscheduler, cron.

Importers supported

  • --from celery: celery app's static app.conf.beat_schedule
  • --from django-celery-beat: PeriodicTask ORM table
  • --from rq: rq-scheduler Redis sorted set
  • --from apscheduler: APScheduler jobstores (3.x and 4.x)
  • --from cron: /etc/crontab and friends (system cron files)

All five include --dry-run (print JSONL) and --verify (diff against brain). Solar schedules (rare in celery-beat) round-trip through the importer and exporter.


Schedule kinds (v1.1)

  • cron: any standard 5-field crontab string
  • interval: 30s, 5m, 2h, 1d (or bare integer seconds)
  • one_shot: fire once at an ISO-8601 timestamp
  • solar: event:lat:lon for sunrise, sunset, dawn, dusk, noon, solar_noon, midnight, solar_midnight at a given location. Backed by astral; polar perpetual-day windows return no fire.

Per-schedule catch-up policy (skip / fire_one_missed / fire_all_missed) is honored at recovery time.


Project status

Engineering scope of v1 GA is closed. Every architectural deliverable is shipped; the four "in the wild" criteria (5+ external production deployments, 90-day soak, public benchmark write-up, first paying inquiry) are calendar-bound on adoption.

If you want zero risk, wait for those references. If you want to be one of the early production users, the migration tools and rollback plan are built. You can be running it in production by end of week.

Open an issue at github.com/z4jdev/z4j-scheduler/issues with your migration story.


See also

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

z4j_scheduler-1.1.2.tar.gz (148.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

z4j_scheduler-1.1.2-py3-none-any.whl (185.6 kB view details)

Uploaded Python 3

File details

Details for the file z4j_scheduler-1.1.2.tar.gz.

File metadata

  • Download URL: z4j_scheduler-1.1.2.tar.gz
  • Upload date:
  • Size: 148.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.4

File hashes

Hashes for z4j_scheduler-1.1.2.tar.gz
Algorithm Hash digest
SHA256 13f0d3b4eebc3a0196e28968864764c831e3ed2c6d007d053df2d312350abbf2
MD5 a73d474492cbb0e9e85081e6810e2581
BLAKE2b-256 2362c01bf9e9da43d29144363e93c33774190994e735ec1c245f1defbc7dbfda

See more details on using hashes here.

File details

Details for the file z4j_scheduler-1.1.2-py3-none-any.whl.

File metadata

  • Download URL: z4j_scheduler-1.1.2-py3-none-any.whl
  • Upload date:
  • Size: 185.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.4

File hashes

Hashes for z4j_scheduler-1.1.2-py3-none-any.whl
Algorithm Hash digest
SHA256 efedbfb56ecbba129c2185086ef03693f063ecedd9fe1774c5b47009e0dc273d
MD5 8940c4dafaecaa1a60f96e64c8903ae6
BLAKE2b-256 08443caf90e3aa889c9081b54469b32f013e4e9ec5c7d753bcafc3d7db495649

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page