Skip to main content

⚡ ZenPulse Scheduler for Django

Modern, Hybrid, and Zero-Latency Job Scheduler

ZenPulse Scheduler is a Developer-First background job scheduler for Django. Unlike traditional schedulers that either rely on heavy external infrastructure (Redis/Celery) or spam your database with polling queries, ZenPulse uses a Smart Hybrid Architecture.


🌟 Features

  • ✅ No Redis/Celery Required - Pure Django solution
  • ✅ Zero DB Impact - Smart file-based signaling system
  • ✅ Hybrid Execution - Config in DB, Runtime in RAM
  • ✅ Selective Logging - Log everything, nothing, or only failures
  • ✅ Live Updates - Change schedules without restarting
  • ✅ Multi-Process Safe - Built-in locking mechanisms
  • ✅ Interval & Cron Support - Flexible scheduling options

📦 Installation

Step 1: Install the Package

pip install zenpulse-scheduler

Or if installing from source:

cd /path/to/package
pip install -e .

Step 2: Add to Django Settings

# settings.py
INSTALLED_APPS = [
    # ... other apps
    'zenpulse_scheduler',
]

Step 3: Run Migrations

python manage.py migrate

🚀 Quick Start Guide

1. Define Your Job

Create a file (e.g., yourapp/jobs.py) and register your job:

from zenpulse_scheduler.registry import zenpulse_job

# Basic: auto-creates with default schedule (every 5 minutes, enabled)
@zenpulse_job("send_daily_report")
def send_daily_report():
    """Send daily sales report via email"""
    print("📧 Sending daily report...")
    # Your business logic here

# Custom interval: runs every 30 seconds
@zenpulse_job("health_check", trigger="interval", interval_value=30, interval_unit="seconds")
def health_check():
    print("💓 Health check...")

# Cron schedule: runs daily at 8:30 AM
@zenpulse_job("morning_report", trigger="cron", cron_hour="8", cron_minute="30")
def morning_report():
    print("📊 Generating morning report...")

🚀 Auto-Sync: When the scheduler starts, it automatically creates a ScheduleConfig entry in the database for any new registered job. No need to manually add it in Django Admin!

Important: Make sure this file is imported when Django starts. You can do this by:

# yourapp/apps.py
from django.apps import AppConfig

class YourAppConfig(AppConfig):
    name = 'yourapp'
    
    def ready(self):
        import yourapp.jobs  # Import to register jobs

Or import it in your urls.py:

# yourapp/urls.py
import yourapp.jobs  # Ensure jobs are registered

urlpatterns = [
    # your urls
]

2. Start the Scheduler

Open a separate terminal and run:

python manage.py run_zenpulse_scheduler

You should see:

Starting ZenPulse Scheduler (Sync: 10s, Lock: False)...
Auto-created ScheduleConfig for job 'send_daily_report' [interval (5 minutes), enabled=True]

3. (Optional) Fine-tune via Admin

Jobs are auto-configured from the decorator defaults. But if you want to change settings later:

  1. Go to Django Admin → ZenPulse Scheduler → Schedule Configs
  2. Edit the auto-created entry
  3. Change schedule, enable/disable, adjust log policy, etc.
  4. Save — changes apply within the next sync interval (no restart needed!)

📚 Complete Configuration Guide

All job scheduling is controlled through the ScheduleConfig model in Django Admin.

Core Fields

Job Key (Required)

  • Type: Text (Unique)
  • Description: Must match the name you used in @zenpulse_job("name")
  • Example: send_daily_report

Enabled (Required)

  • Type: Checkbox
  • Description: Controls whether the job runs
  • Default: ✅ Checked (Enabled)
  • Behavior:
    • ✅ Checked → Job runs according to schedule
    • ❌ Unchecked → Job stops immediately (within next sync interval)

Trigger Configuration

Trigger Type (Required)

Choose how your job should be scheduled:

Choice When to Use Example
Interval Repeating tasks that run in a loop "Check for new emails every 5 minutes"
Cron Tasks that run at specific calendar times "Send report every Monday at 9 AM"

Interval Configuration

Use when Trigger Type = Interval

Interval Value (Required for Interval)

  • Type: Number
  • Description: How many units to wait between runs
  • Example: 30 (combined with unit below)

Interval Unit (Required for Interval)

Unit Best For Example Usage
Seconds High-frequency monitoring, heartbeats 30 seconds = runs every 30 seconds
Minutes Standard periodic tasks 15 minutes = runs every 15 minutes
Hours Regular updates, data sync 4 hours = runs every 4 hours
Days Daily routines 1 day = runs once per day
Weeks Weekly maintenance 2 weeks = runs every 2 weeks

Example Configurations:

Interval Value: 10, Unit: Minutes → Runs every 10 minutes
Interval Value: 1, Unit: Hours → Runs every hour
Interval Value: 30, Unit: Seconds → Runs every 30 seconds

Cron Configuration

Use when Trigger Type = Cron

All cron fields support:

  • Specific values (e.g., 8 for 8 AM)
  • Wildcards (* means "every")
  • Lists (e.g., mon,wed,fri)
  • Ranges (e.g., 1-5)

Cron Minute

  • Valid Values: 0-59 or *
  • Default: * (every minute)
  • Examples:
    • 0 = At the top of the hour
    • 30 = At 30 minutes past the hour
    • */15 = Every 15 minutes

Cron Hour

  • Valid Values: 0-23 or *
  • Default: * (every hour)
  • Examples:
    • 9 = 9 AM
    • 14 = 2 PM
    • 22 = 10 PM

Cron Day (of Month)

  • Valid Values: 1-31 or *
  • Default: * (every day)
  • Examples:
    • 1 = 1st of the month
    • 15 = 15th of the month

Cron Month

  • Valid Values: 1-12 or *
  • Default: * (every month)
  • Examples:
    • 1 = January
    • 12 = December

Cron Day of Week

  • Valid Values: 0-6 (0=Sunday) or mon,tue,wed,thu,fri,sat,sun or *
  • Default: * (every day)
  • Examples:
    • mon = Every Monday
    • 0 = Every Sunday
    • mon,wed,fri = Monday, Wednesday, Friday

Example Cron Configurations:

Daily at 8:30 AM:
  Minute: 30, Hour: 8, Day: *, Month: *, Day of Week: *

Every Monday at 9 AM:
  Minute: 0, Hour: 9, Day: *, Month: *, Day of Week: mon

First day of every month at midnight:
  Minute: 0, Hour: 0, Day: 1, Month: *, Day of Week: *

Every weekday at 6 PM:
  Minute: 0, Hour: 18, Day: *, Month: *, Day of Week: mon,tue,wed,thu,fri

Advanced Options

Max Instances

  • Type: Number
  • Default: 1
  • Description: Maximum number of this job that can run simultaneously
  • Use Case: Set to 3 if you want to allow up to 3 parallel executions

Coalesce

  • Type: Checkbox
  • Default: ✅ Checked
  • Description: If multiple runs were missed (e.g., scheduler was down), combine them into one
  • Behavior:
    • ✅ Checked → Run once to catch up
    • ❌ Unchecked → Run all missed executions

Misfire Grace Time

  • Type: Number (seconds)
  • Default: 60
  • Description: How long after the scheduled time the job can still run
  • Example: If job was supposed to run at 10:00 but scheduler was busy, it can still run until 10:01 (60 seconds grace)

Logging Configuration

Log Policy

Control how much execution history is saved to the database:

Policy What Gets Logged Database Impact Best For
None Nothing Zero writes High-frequency jobs (every few seconds)
Failures Only Only when job crashes Minimal writes Production critical jobs (recommended)
All Executions Every success and failure High writes Debugging, auditing

Recommendation: Use Failures Only for production. This way:

  • ✅ You get full error traces when something breaks
  • ✅ Database stays clean
  • ✅ You can still monitor job health

🖥️ Admin Panel Examples

Example 1: Send Email Every 30 Minutes

Job Key: send_email_digest
Enabled: ✅
Trigger Type: Interval
Interval Value: 30
Interval Unit: Minutes
Log Policy: Failures Only

Example 2: Daily Report at 8:30 AM

Job Key: generate_daily_report
Enabled: ✅
Trigger Type: Cron
Cron Minute: 30
Cron Hour: 8
Cron Day: *
Cron Month: *
Cron Day of Week: *
Log Policy: All Executions

Example 3: Weekly Cleanup Every Sunday at Midnight

Job Key: weekly_cleanup
Enabled: ✅
Trigger Type: Cron
Cron Minute: 0
Cron Hour: 0
Cron Day: *
Cron Month: *
Cron Day of Week: sun
Log Policy: Failures Only

⚙️ Running the Scheduler

Development

python manage.py run_zenpulse_scheduler

Production (with Safety Lock)

python manage.py run_zenpulse_scheduler --lock

The --lock flag prevents multiple scheduler instances from running simultaneously:

  • PostgreSQL: Uses advisory locks (recommended)
  • MySQL: Uses GET_LOCK
  • SQLite/Others: Uses file-based locking

Custom Sync Interval

By default, the scheduler checks for config changes every 10 seconds. To reduce database queries:

python manage.py run_zenpulse_scheduler --sync-every 60

This checks only once per minute. Trade-off: Changes take up to 60 seconds to apply.


🏗️ Architecture

┌─────────────────┐
│  Django Admin   │  ← You configure jobs here
└────────┬────────┘
         │ Save Config
         ↓
┌─────────────────┐
│    Database     │  ← Stores ScheduleConfig
└────────┬────────┘
         │ Sync (every 10s)
         ↓
┌─────────────────┐
│   Scheduler     │  ← Runs in separate process
│   (Memory)      │
└────────┬────────┘
         │ Execute
         ↓
┌─────────────────┐
│   Your Job      │  ← @zenpulse_job decorated function
└─────────────────┘

Key Points:

  1. Configuration lives in Database (persistent)
  2. Execution happens in Memory (fast)
  3. Scheduler syncs config changes automatically
  4. No Redis or external dependencies needed

📊 Monitoring Job Execution

View Execution Logs

Go to Django Admin → ZenPulse Scheduler → Job Execution Logs

You'll see:

  • Job Key: Which job ran
  • Status: Success or Fail
  • Run Time: When it executed (with seconds precision)
  • Duration: How long it took (in milliseconds)
  • Exception Details: Full traceback if it failed

Understanding Log Entries

Job Key: send_email_digest
Status: Success
Run Time: 2026-02-01 14:30:45
Duration: 234.5 ms

If a job fails:

Job Key: send_email_digest
Status: Fail
Run Time: 2026-02-01 14:30:45
Exception Type: SMTPException
Exception Message: Connection refused
Traceback: [Full Python traceback here]

🔧 Troubleshooting

Job Not Running?

  1. Check if job is registered:

    # In Django shell
    from zenpulse_scheduler.registry import JobRegistry
    print(JobRegistry.get_all_jobs())
    

    Your job should appear in the list.

  2. Check if enabled in Admin:

    • Go to Schedule Configs
    • Verify Enabled is checked
  3. Check scheduler is running:

    • Look for Starting ZenPulse Scheduler... in terminal
    • Check for any error messages

Duplicate Executions?

  • Make sure only one run_zenpulse_scheduler process is running
  • Use --lock flag in production
  • Check if you accidentally started it in multiple terminals

Jobs Running at Wrong Time?

  • Verify your TIME_ZONE setting in Django settings
  • Check cron configuration carefully
  • Use Interval for simple repeating tasks

Database Performance Issues?

  • Set Log Policy to None or Failures Only
  • Increase --sync-every to reduce config checks
  • Consider archiving old logs periodically

🚀 Production Deployment

Using Systemd (Linux)

Create /etc/systemd/system/zenpulse-scheduler.service:

[Unit]
Description=ZenPulse Scheduler
After=network.target

[Service]
Type=simple
User=www-data
WorkingDirectory=/path/to/your/project
ExecStart=/path/to/venv/bin/python manage.py run_zenpulse_scheduler --lock --sync-every 30
Restart=always
RestartSec=10

[Install]
WantedBy=multi-user.target

Enable and start:

sudo systemctl enable zenpulse-scheduler
sudo systemctl start zenpulse-scheduler
sudo systemctl status zenpulse-scheduler

Using Supervisor

[program:zenpulse-scheduler]
command=/path/to/venv/bin/python manage.py run_zenpulse_scheduler --lock
directory=/path/to/your/project
user=www-data
autostart=true
autorestart=true
redirect_stderr=true
stdout_logfile=/var/log/zenpulse-scheduler.log

Using Docker

# In your Dockerfile
CMD ["python", "manage.py", "run_zenpulse_scheduler", "--lock"]

Or in docker-compose.yml:

services:
  scheduler:
    build: .
    command: python manage.py run_zenpulse_scheduler --lock
    depends_on:
      - db

❓ FAQ

Q: Can I run this with Gunicorn/Uvicorn?
A: Yes, but run the scheduler in a separate process/container. Never run it inside web workers.

Q: What happens if I restart the server?
A: Configuration is in the database, so it persists. Just start the scheduler command again.

Q: Can jobs accept parameters?
A: Currently, jobs should be parameter-less functions. For different parameters, create separate job functions.

Q: How do I delete old logs?
A: You can manually delete from Admin or create a cleanup job:

@zenpulse_job("cleanup_old_logs")
def cleanup_old_logs():
    from datetime import timedelta
    from django.utils import timezone
    from zenpulse_scheduler.models import JobExecutionLog
    
    cutoff = timezone.now() - timedelta(days=30)
    JobExecutionLog.objects.filter(run_time__lt=cutoff).delete()

Q: Is this production-ready?
A: Yes! It's built on APScheduler (battle-tested library) with Django best practices.


📝 License

[Your License Here]

🤝 Contributing

Contributions welcome! Please open an issue or PR.


Made with ❤️ for Django developers who want simple, powerful scheduling.

Metadata

Release files for zenpulse-scheduler 0.1.6

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for zenpulse-scheduler 0.1.6
File Size Uploaded
zenpulse_scheduler-0.1.6.tar.gz 20.8 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for zenpulse-scheduler 0.1.6
File Interpreter ABI Platform
zenpulse_scheduler-0.1.6-py3-none-any.whl Python 3 none any Details

Total release size: 39.7 kB

Release files / zenpulse_scheduler-0.1.6.tar.gz

Download URL zenpulse_scheduler-0.1.6.tar.gz
Size 20.8 kB
Tags Source
SHA-256 checksum
How to use checksums
7fd6d67fb74a8d1427dd80f0bd1282e12301ce0e127a73abf660dcec23c9e743
BLAKE2b-256 checksum
How to use checksums
a84967e6279126b35cab3793de11a3f9818016b063170018aade6fe97de2b57f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Mar 3, 2026.

Transparency log

Release files / zenpulse_scheduler-0.1.6-py3-none-any.whl

Download URL zenpulse_scheduler-0.1.6-py3-none-any.whl
Size 18.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
81db75e42924747ad4d297046c0ef22ca09978fe8eea03c04168c37673db3592
BLAKE2b-256 checksum
How to use checksums
00fb15760869e3ac8124167257f862af682e5474f8c57920b8e78e809a251510
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Mar 3, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.1.6 This release

2 release files

0.1.4

2 release files

0.1.2

2 release files

0.1.1

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page