Procrastinate
Procrastinate is an open-source Python 3.10+ distributed task processing library that leverages PostgreSQL 13+ to store task definitions, manage locks, and dispatch tasks. It integrates with both synchronous and asynchronous code, offers Django integration, and is compatible with ASGI frameworks, supporting features like periodic tasks, retries, and arbitrary task locks.
Warnings
- breaking The database migration process has significantly changed in major versions (e.g., around v1.0). Migrations are now categorized as 'pre' and 'post', requiring specific application before and after deploying new Procrastinate code. Ensure you consult the migration documentation for your upgrade path.
- breaking Python 3.9 support has been dropped in recent versions (e.g., 3.7.0), while support for Python 3.14 has been added. Ensure your environment uses a supported Python version (3.10+).
- gotcha Workers must be aware of all task definitions. If tasks are in modules not directly imported by your `App` object or not specified in `App(import_paths=...)`, the worker will fail with a `TaskNotFound` error when attempting to execute such tasks.
- gotcha Procrastinate is asynchronous at its core. While it offers synchronous deferral methods, running workers or interacting with the CLI requires an asynchronous context. Synchronous task functions executed by an async worker run in a thread, impacting parallelism for CPU-bound tasks due to the GIL.
Install
-
pip install procrastinate psycopg[binary] -
pip install procrastinate aiopg
Imports
- App
from procrastinate import App
- PsycopgConnector
from procrastinate import PsycopgConnector
Quickstart
import random
import time
import os
from procrastinate import App, PsycopgConnector
# Configure your PostgreSQL connection string
# Example: 'postgresql://user:password@host:port/database'
# Use os.environ.get for production readiness
POSTGRES_DSN = os.environ.get('POSTGRES_DSN', 'postgresql://postgres:password@localhost:5432/postgres')
app = App(
connector=PsycopgConnector(
dsn=POSTGRES_DSN
)
)
@app.task(name="sum_task")
def sum_task(a, b):
time.sleep(random.random() * 0.5) # Simulate work
result = a + b
print(f"Task sum_task({a}, {b}) finished with result: {result}")
return result
async def main():
print("Applying schema...")
await app.open_async()
await app.get_connector().check()
await app.get_connector().execute_query_one("SELECT 1 FROM procrastinate_jobs LIMIT 1")
print("Schema applied and checked.")
print("Deferring a job...")
job = await sum_task.defer_async(a=3, b=5)
print(f"Job deferred with ID: {job.id}")
await app.close_async()
if __name__ == "__main__":
import asyncio
asyncio.run(main())
# To run the worker, save the above as e.g., 'my_tasks.py' and execute in a separate terminal:
# export POSTGRES_DSN='postgresql://postgres:password@localhost:5432/postgres' # (or your actual DSN)
# procrastinate --app=my_tasks.app worker