FastScheduler

repository·main·Indexed 19 days ago

https://github.com/michielme/fastscheduler

A lightweight, async-capable Python task scheduler supporting cron expressions, timezones, and persistent state via JSON or SQLModel (SQLite, PostgreSQL, MySQL). It includes a real-time FastAPI dashboard for monitoring and managing jobs, with features for job timeouts, automatic retries with exponential backoff, and a dead letter queue for failed tasks.

Tokens
10.9K
Snippets
42
Records
46
Agent score
65%

What's inside fastscheduler

  1. Integrate FastScheduler with FastAPI

    main

    Add a real-time monitoring dashboard to your FastAPI application using create_scheduler_routes. The dashboard is accessible via the routes you include in your app.

    from fastapi import FastAPI
    from fastscheduler import FastScheduler
    from fastscheduler.fastapi_integration import create_scheduler_routes
    
    app = FastAPI()
    scheduler = FastScheduler(quiet=True)
    
    # Add dashboard at /scheduler/
    app.include_router(create_scheduler_routes(scheduler))
    
    @scheduler.every(30).seconds
    def background_task():
        print("Background work")
    
    scheduler.start()
  2. Install FastScheduler

    main

    Install the core package using pip. You can also install optional extras for specific features like the FastAPI dashboard, cron expression support, or database persistence.

    # Basic installation
    pip install fastscheduler
    
    # With FastAPI dashboard
    pip install fastscheduler[fastapi]
    
    # With cron expression support
    pip install fastscheduler[cron]
    
    # With database support (SQLite, PostgreSQL, MySQL)
    pip install fastscheduler[database]
    
    # All features
    pip install fastscheduler[all]
    # Basic installation
    pip install fastscheduler
    
    # With FastAPI dashboard
    pip install fastscheduler[fastapi]
    
    # With cron expression support
    pip install fastscheduler[cron]
    
    # With database support (SQLite, PostgreSQL, MySQL)
    pip install fastscheduler[database]
    
    # All features
    pip install fastscheduler[all]
  3. Use Database Storage for production

    main

    For production workloads requiring transactional integrity, use the sqlmodel storage backend with a database like SQLite, PostgreSQL, or MySQL. Requires pip install fastscheduler[database].

    # SQLite
    scheduler = FastScheduler(
        storage="sqlmodel",
        database_url="sqlite:///scheduler.db"
    )
    
    # PostgreSQL
    scheduler = FastScheduler(
        storage="sqlmodel",
        database_url="postgresql://user:password@localhost:5432/mydb"
    )
    
    # MySQL
    scheduler = FastScheduler(
        storage="sqlmodel",
        database_url="mysql://user:password@localhost:3306/mydb"
    )
  4. Quick Start with FastScheduler

    main

    To get started, import FastScheduler, define tasks using decorators, and call .start(). The API supports both standard functions and async functions.

    from fastscheduler import FastScheduler
    
    scheduler = FastScheduler(quiet=True)
    
    @scheduler.every(10).seconds
    def task():
        print("Task executed")
    
    @scheduler.daily.at("14:30")
    async def daily_task():
        print("Daily task at 2:30 PM")
    
    scheduler.start()
  5. Integrate FastScheduler with FastAPI using Lifespan

    main

    To use FastScheduler within a FastAPI application, use the lifespan pattern to ensure the scheduler starts when the app starts and stops gracefully when the app shuts down. Use create_scheduler_routes(scheduler) to expose scheduler management via API routes.

    from contextlib import asynccontextmanager
    from fastapi import FastAPI
    from fastscheduler import FastScheduler
    from fastscheduler.fastapi_integration import create_scheduler_routes
    
    scheduler = FastScheduler(quiet=True)
    
    @asynccontextmanager
    async def lifespan(app: FastAPI):
        scheduler.start()
        yield
        scheduler.stop(wait=True)
    
    app = FastAPI(lifespan=lifespan)
    app.include_router(create_scheduler_routes(scheduler))
    
    @scheduler.every(30).seconds
    def background_job():
        print("Working...")
  6. Configure FastScheduler settings

    main

    The FastScheduler constructor accepts several configuration parameters to control persistence, performance, and history:

    • state_file: Path to the JSON persistence file (default: fastscheduler_state.json).
    • storage: Backend type, either "json" (default) or "sqlmodel".
    • database_url: Connection string for the sqlmodel backend.
    • quiet: If True, suppresses log messages.
    • auto_start: If True, starts the scheduler immediately.
    • max_history: Maximum number of execution history entries to keep (default: 10000).
    • max_workers: Number of concurrent job threads (default: 10).
    • history_retention_days: Number of days to keep history before deletion (default: 7).
    • max_dead_letters: Maximum failed jobs to keep in the Dead Letter Queue (default: 500).
    scheduler = FastScheduler(
        state_file="scheduler.json",    # Persistence file for JSON backend
        storage="json",                 # Storage backend: "json" or "sqlmodel"
        database_url=None,              # Database URL for sqlmodel backend
        quiet=True,                     # Suppress log messages
        auto_start=False,               # Start immediately
        max_history=5000,               # Max history entries to keep
        max_workers=20,                 # Concurrent job threads
        history_retention_days=8,       # Delete history older than X days
        max_dead_letters=500,           # Max failed jobs in dead letter queue
    )
  7. Initialize FastScheduler with different storage backends

    main

    The FastScheduler class can be initialized with several storage options to persist job state, history, and statistics.

    • JSON (Default): Uses a local JSON file. Requires state_file parameter.
    • SQLModel: Uses a relational database. Requires storage="sqlmodel" and a database_url (e.g., SQLite, PostgreSQL, or MySQL).
    • Custom: Pass an instance of a class implementing the StorageBackend interface.

    Configuration options include max_workers (thread pool size), max_history (limit on history entries), and history_retention_days (age limit for history).

    # JSON storage (default)
    scheduler = FastScheduler(state_file="scheduler.json")
    
    # SQLite database via SQLModel
    scheduler = FastScheduler(
        storage="sqlmodel",
        database_url="sqlite:///scheduler.db"
    )
    
    # PostgreSQL database via SQLModel
    scheduler = FastScheduler(
        storage="sqlmodel",
        database_url="postgresql://user:pass@localhost/mydb"
    )
    
    # Custom storage backend
    scheduler = FastScheduler(storage=MyCustomStorageBackend())
  8. Integrate FastScheduler with FastAPI

    main

    To add a web-based monitoring dashboard and management API to your FastAPI application, use create_scheduler_routes. This function returns an APIRouter containing several endpoints for viewing statistics, managing jobs (pause, resume, cancel, run now), and inspecting job history or dead letters.

    Installation Requirement: You must install the FastAPI extra for this integration to work:

    pip install fastscheduler[fastapi]

    Default Configuration:

    • The default URL prefix is /scheduler.
    • The default OpenAPI tag is scheduler.
    • You can override these by passing prefix and tags as keyword arguments to create_scheduler_routes.
    from fastapi import FastAPI
    from fastscheduler import FastScheduler
    from fastscheduler.fastapi_integration import create_scheduler_routes
    
    app = FastAPI()
    scheduler = FastScheduler()
    
    # Include the scheduler routes in your FastAPI app
    app.include_router(create_scheduler_routes(scheduler))
    
    scheduler.start()
  9. Complete usage example for FastScheduler

    main

    This example demonstrates how to define various types of jobs (interval, async with timezone, cron with retries, and weekly) and manage the scheduler lifecycle using start() and stop().

    import asyncio
    import time
    from fastscheduler import FastScheduler
    
    scheduler = FastScheduler(quiet=True)
    
    # Simple interval job
    @scheduler.every(10).seconds
    def heartbeat():
        print(f"[{time.strftime('%H:%M:%S')}] ❤️ Heartbeat")
    
    # Async job with timezone
    @scheduler.daily.at("09:00", tz="America/New_York").timeout(60)
    async def morning_report():
        print("Generating report...")
        await asyncio.sleep(5)
        print("Report sent!")
    
    # Cron job with retries
    @scheduler.cron("*/5 * * * *").retries(3)
    def check_api():
        print("Checking API health")
    
    # Weekly job
    @scheduler.weekly.monday.at("10:00")
    def weekly_standup():
        print("Time for standup!")
    
    # Start scheduler
    scheduler.start()
    
    try:
        while True:
            time.sleep(60)
            scheduler.print_status()
    except KeyboardInterrupt:
        scheduler.stop()
  10. Configure timezones for jobs

    main

    You can specify a timezone using the tz parameter in .at() or by chaining the .tz("timezone") method. This is useful for scheduling tasks in specific global regions.

    # Using the tz parameter
    @scheduler.daily.at("09:00", tz="America/New_York")
    def nyc_morning():
        print("Good morning, New York!")
    
    # Using the .tz() method (chainable)
    @scheduler.weekly.monday.tz("Europe/London").at("09:00")
    def london_standup():
        print("Monday standup")
    
    # With cron expressions
    @scheduler.cron("0 9 * * MON-FRI").tz("Asia/Tokyo")
    def tokyo_market():
        print("Tokyo market open")
  11. Manage job execution state (Pause, Resume, Cancel)

    main

    Control running or scheduled jobs using the FastScheduler instance methods:

    • pause_job(job_id): Keeps the job in the queue but prevents execution.
    • resume_job(job_id): Resumes a paused job.
    • cancel_job(job_id): Removes a job from the scheduler.
    • cancel_job_by_name(func_name): Cancels all jobs associated with a specific function name.
    # Pause a job (stays in queue but won't execute)
    scheduler.pause_job("job_0")
    
    # Resume a paused job
    scheduler.resume_job("job_0")
    
    # Cancel and remove a job
    scheduler.cancel_job("job_0")
    
    # Cancel all jobs with a specific function name
    scheduler.cancel_job_by_name("my_task")