DevOps, coding & IT infrastructures

Scheduling tasks is an essential part of any web application, especially those that require periodic or delayed actions. There are many ways to schedule tasks in Python, each with strengths and weaknesses. In this article, we'll look at some of the most popular ways to schedule tasks in a FastAPI app.

sched - Event scheduler from Python

The sched module is part of Python's standard library and provides a simple way to schedule events in a program. While it can be used in a FastAPI app, it is not recommended due to its simplicity and limited functionality.

Here's an example of using sched to schedule a task in a FastAPI app:

import sched
import time

scheduler = sched.scheduler(time.time, time.sleep)

def run_me_every_minute():
    print("Running every minute...")

def schedule_next_event():
    scheduler.enter(60, 1, run_me_every_minute)
    scheduler.run()

scheduler.enter(60, 1, run_me_every_minute)
while True:
    schedule_next_event()

In the above code, we create a scheduler object using the sched module and define a function run_me_every_minute to run as a scheduled task. We then use the enter method to schedule the task to run after 60 seconds and the run method to start the scheduler.

While sched is simple and easy to use, but lacks some of the advanced features of other scheduling libraries.

schedule python package


The schedule package (-> Github, -> Docs) describes itself as "Python job scheduling for humans." It provides a  powerful and flexible way to schedule tasks in Python. It is easy to use and has a simple API.

Here's an example of using schedule to schedule a task in a FastAPI app:

import schedule
import time

def run_me_every_minute():
    print("Running every minute...")

schedule.every(1).minutes.do(run_me_every_minute)

while True:
    schedule.run_pending()
    time.sleep(1)

In the above code, we define a function run_me_every_minute to run as a scheduled task and use the every method to schedule the task to run every minute. We then use the run_pending method to check for scheduled tasks and the sleep method to delay the loop for 1 second.

While schedule is easy to use and provides many advanced features such as error handling and interval customization; it is limited to running on a single thread and may not be suitable for high-performance applications.

Using repeated tasks from fastapi-utils

The fastapi-utils package (-> Github, -> Docs) provides a simple and convenient way to schedule repeated tasks in a FastAPI app. It uses the asyncio library to handle asynchronous tasks.

Here's an example of using fastapi-utils to schedule a task in a FastAPI app:

from fastapi import FastAPI
from fastapi_utils.tasks import repeat_every

app = FastAPI()

@repeat_every(seconds=60)
async def run_me_every_minute():
    print("Running every minute...")

if __name__ == "__main__":
    import uvicorn
    uvicorn.run(app)

In the above code, we use the repeat_every decorator to schedule a task to run every 60 seconds. We then define a function run_me_every_minute to run as the scheduled task.

fastapi-utils is easy to use and integrates well with FastAPI, but it may not be suitable for more complex scheduling tasks.

Using package arq

The arq package (-> Github, -> Docs) provides a powerful and flexible way to schedule tasks in a FastAPI app. It is built on top of the asyncio library and supports advanced features such as task priorities and retry strategies.

Here's an example of using arq to schedule a task in a FastAPI app:

from fastapi import FastAPI
from arq import create_pool
from arq.jobs import Job

app = FastAPI()

async def run_me_every_minute():
    print("Running every minute...")

@app.on_event("startup")
async def startup():
    redis_settings = {"host": "localhost", "port": 6379}
    pool = await create_pool(redis_settings)
    job = Job(run_me_every_minute)
    await pool.enqueue_job(job)

if __name__ == "__main__":
    import uvicorn
    uvicorn.run(app)

In the above code, we use the create_pool function to create a connection pool to Redis, which is used by arq to store and schedule tasks. We then define a function run_me_every_minuteto run as the scheduled task and create a Job object to encapsulate the task. Finally, we use the enqueue_job method to schedule the task to run.

arq is a powerful and flexible scheduling library that can handle complex scheduling tasks, but it requires more setup and configuration than some of the other libraries.

Using celery

Celery is a popular distributed task queue that can be used to schedule tasks in a FastAPI app. It supports various advanced features such as task priorities, retry strategies and task result storage. You find more about the Periodic tasks in the Celery documentation.

Here's an example of using Celery to schedule a task in a FastAPI app:

from fastapi import FastAPI
from celery import Celery

app = FastAPI()
celery = Celery('tasks', broker='pyamqp://guest@localhost//')

@celery.task
def run_me_every_minute():
    print("Running every minute...")

@app.on_event("startup")
async def startup():
    celery.conf.beat_schedule = {
        "run-every-minute": {
            "task": "main.run_me_every_minute",
            "schedule": crontab(minute="*")
        }
    }
    celery.conf.timezone = "UTC"
    celery.conf.task_routes = {"main.run_me_every_minute": {"queue": "default"}}
    celery.autodiscover_tasks(["main"])
    # Other Celery config


if __name__ == "__main__":
    import uvicorn
    uvicorn.run(app)

In the above code, we use the celery.schedules.crontab method to schedule the run_me_every_minute task to run every minute. We also define additional Celery configuration settings for timezone and task routing.

Celery is a powerful and feature-rich scheduling library that can handle complex scheduling tasks, but it requires more setup and configuration than some of the other libraries.

Using Dramatiq

Dramatiq is a high-performance distributed task-processing library that can schedule tasks in a FastAPI app. It supports advanced features such as task priorities, retry strategies, and result storage.

Here's an example of using Dramatiq to schedule a task in a FastAPI app:

from fastapi import FastAPI
from dramatiq import pipeline, actor, run_pipeline, cron

app = FastAPI()

@actor(cron("*/1 * * * *"))
def run_me_every_minute():
    print("Running every minute...")

@app.on_event("startup")
async def startup():
    job = pipeline(run_me_every_minute.message())
    run_pipeline(job)

if __name__ == "__main__":
    import uvicorn
    uvicorn.run(app)

n the above code, we use the dramatiq.cron method to schedule the run_me_every_minute task to run every minute. The cron schedule is defined using the */1 * * * * expression, which means "every minute". We then use the pipeline and run_pipeline methods to schedule and execute the task.

Dramatiq is a powerful and high-performance scheduling library that can handle complex scheduling tasks, but it requires more setup and configuration than other libraries.

APScheduler

The APScheduler library is a popular and powerful scheduling library for Python. It provides many advanced features such as cron-style scheduling, interval scheduling, and more.

Here's an example of using APScheduler to schedule a task to run every minute in a FastAPI app:

from fastapi import FastAPI
from apscheduler.schedulers.asyncio import AsyncIOScheduler

app = FastAPI()

scheduler = AsyncIOScheduler()

async def run_me_every_minute():
    print("Running every minute...")

@app.on_event("startup")
async def startup():
    scheduler.add_job(run_me_every_minute, "interval", minutes=1)
    scheduler.start()

if __name__ == "__main__":
    import uvicorn
    uvicorn.run(app)

In the above code, we use the APScheduler library to schedule a task run_me_every_minute to run every minute. We define a scheduler object and use the add_job method to add the task to the scheduler. The start method is then called to start the scheduler.

Note that we use the AsyncIOScheduler class to create an asynchronous scheduler. This is necessary when using APScheduler with FastAPI, as it is an asynchronous web framework.

In summary, APScheduler is a powerful scheduling library that provides many advanced features. It is compatible with FastAPI and can be used to schedule tasks to run at various intervals.

Conclusion

There are many ways to schedule tasks in a FastAPI app using Python, each with strengths and weaknesses. Choosing the right scheduling library depends on the complexity of your application and the specific features you need.

If you need a simple and easy-to-use scheduling library, the schedule package or fastapi-utils may be a good choice. If you need more advanced features and greater flexibility, arq, Celery, or Dramatiq may be more suitable.

No matter which library you choose, scheduling tasks can greatly enhance the functionality and efficiency of your FastAPI app. With the code examples provided in this article, you should have a good starting point for implementing task scheduling in your own projects.

You’ve successfully subscribed to Coding with passion
Welcome back! You’ve successfully signed in.
Great! You’ve successfully signed up.
Success! Your email is updated.
Your link has expired
Success! Check your email for magic link to sign-in.