A flexible asynchronous job scheduler with distributed mode, multiple input sources, and runtime control
Project description
Espresso Job Scheduler
This project consists of asynchronous job scheduling which can be customized inside a Python script or loaded from a YAML File.
Features
- Flexible Scheduling: Support for cron, interval, one-off, and on-demand job execution
- Multiple Input Sources: List-based and RabbitMQ inputs (extensible)
- Runtime Control: REST API for managing jobs at runtime (pause, resume, trigger, etc.)
- Execution Tracking: Built-in metrics for job execution statistics
- Async-First: Built on asyncio for high-performance concurrent execution
- Retry Logic: Automatic retry with configurable delays
- Worker Pool: Configurable concurrent worker execution
- 🌐 Distributed Mode: Run on multiple servers with Redis coordination (no duplicate jobs!)
Quick Start
Install dependencies:
pip install -r requirements.txt
Run the scheduler with API:
python server.py
The scheduler will start along with a REST API on http://localhost:8000. Visit http://localhost:8000/docs for interactive API documentation.
Basic Usage
For Example:
Creating a YAML configuration containing a simple RabbitMQ queue with a job definition to act on it will look as follows
# jobs_definitions/rabbit_mq_jobs.yaml
inputs:
- id: rabbit_orders
type: rabbitmq
url: amqp://guest:guest@rabbitmq:5672/
queue: orders_queue
prefetch_count: 10
jobs:
- id: process_orders_job
type: espresso_job
module: testing.test
function: process_order
trigger:
kind: input
input_id: rabbit_orders
schedule:
kind: on_demand
And then load and run the jobs.
# main.py
async def main():
inputs, jobs = yaml_loader.load_jobs_from_yaml(
"jobs_definitions/rabbit_mq_jobs.yaml"
)
sched = EspressoScheduler(jobs, inputs)
await sched.run_forever()
The above could basically be written as follows (if we decide to manually write the inputs in python)
# main.py
async def main():
inputs = EspressoRabbitMQInputDefinition(
id="rabbit_orders",
type="rabbitmq",
url="amqp://guest:guest@rabbitmq:5672/",
prefetch_count=10
)
jobs = EspressoJobDefinition(
id="process_orders_job",
type="espresso_job",
module="testing.test",
function="process_order",
trigger=EspressoTrigger(
kind="input",
input_id="rabbit_orders" # note: this has to be equal to the espresso input's id or else it won't be matched
),
schedule="on_demand"
)
sched = EspressoScheduler(jobs, inputs)
await sched.run_forever()
🌐 Distributed Mode (NEW!)
Run Espresso on multiple servers with automatic load distribution!
Enable distributed coordination with just one parameter:
sched = EspressoScheduler(
jobs,
inputs,
redis_url="redis://localhost:6379" # 👈 Add this!
)
Now you can run the same scheduler on multiple servers:
- ✅ Jobs execute exactly once (no duplicates)
- ✅ Automatic failover if a server crashes
- ✅ Load distributes across all servers
- ✅ Shared state via Redis
📖 Full guide: DISTRIBUTED_SETUP.md
🧪 Quick test:
python examples/test_distributed.py
Runtime Control
The scheduler includes a REST API for runtime job management. See RUNTIME_CONTROL.md for detailed documentation.
Available API Endpoints
GET /jobs- List all jobs with status and metricsGET /jobs/{job_id}- Get specific job detailsPOST /jobs/{job_id}/pause- Pause a jobPOST /jobs/{job_id}/resume- Resume a paused jobPOST /jobs/{job_id}/stop- Stop a jobPOST /jobs/{job_id}/enable- Enable a disabled jobPOST /jobs/{job_id}/trigger- Manually trigger job executionGET /health- Scheduler health check
Example: Control Jobs via API
import requests
# Pause a job
requests.post("http://localhost:8000/jobs/process_orders_job/pause")
# Manually trigger a job
requests.post("http://localhost:8000/jobs/process_orders_job/trigger")
# Check job status
response = requests.get("http://localhost:8000/jobs/process_orders_job")
job = response.json()
print(f"Executions: {job['execution_count']}, Status: {job['status']}")
Run the demo:
python examples/runtime_control_demo.py
The different types of jobs and inputs currently supported
Job Types
- on_demand - Will act on input the moment it sees it. For queues, think of it as popping the moment it pushes
- interval - Every x seconds, will grab a batch and act upon it
- cron - Same as cron jobs. Run the function at a specific moment (9:00AM every weekday = 0 9 * * 1-5)
- Format: minute - hour - day of the month - month - weekday
- one_off - Run once at a specific datetime
Input Types
- Lists (EspressoListInputDefinition) - Process items from a static list
- RabbitMQ (EspressoRabbitMQInputDefinition) - Consume messages from RabbitMQ queues
Job States
- active - Job is running normally according to schedule
- paused - Job is temporarily paused (can be resumed)
- stopped - Job is stopped
- disabled - Job is disabled (typically after exceeding max retries)
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file espresso_scheduler-0.1.0.tar.gz.
File metadata
- Download URL: espresso_scheduler-0.1.0.tar.gz
- Upload date:
- Size: 17.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
014e4f2642ccc5affeaa35fed09ae2e201f023204fb84c80b3a0dbe1e16f8443
|
|
| MD5 |
354613b30863501955f79b47cda3434d
|
|
| BLAKE2b-256 |
4fb77fccb7535497553d91f501dd6672c34077b7c5cbbc2c10d3274d250edf9b
|
File details
Details for the file espresso_scheduler-0.1.0-py3-none-any.whl.
File metadata
- Download URL: espresso_scheduler-0.1.0-py3-none-any.whl
- Upload date:
- Size: 22.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
83b1420a83ea44ee51fd77a7867b8624fa393a284fb563d6987997270d32eeb2
|
|
| MD5 |
55ee71e9bad8d7dc483c8f15c59b056d
|
|
| BLAKE2b-256 |
f9becdc052533540f1986c7bdfac1c532df6f505c17e7e53b2cac07e85827103
|