0

I have an FastAPI server with background tasks, which process long wait until some time and then do push notification. I want to recreate these tasks, when server restart. All information, that I need, store in database, so I can just create BackgroundTasks with list of BackgroundTask instances again in some on_event('startup') method, but I have no idea, how to attach BackgroundTasks instance to app, or something like this. I saw the solution with request object, but how can I do it with entire BackgroundTasks instance and in on_event method?

I don't want to use something like Celery, because I need such logic only in one small part of project. There will be no other such background tasks.

Dmitriy Lunev
  • 177
  • 2
  • 12
  • What do you mean with `I have no idea, how to attach BackgroundTasks instance to app` ? – lsabi Apr 25 '23 at 16:18
  • @lsabi when I make `.add_task` in `BackgroundTasks` instance, it just append task in list, and do nothing. I guess, application class run these tasks after adding, so think, that it possible to attach background tasks to application, and then it automatically run them. Am I wrong? – Dmitriy Lunev Apr 25 '23 at 17:50
  • I've made small research and realize, that in depth, background tasks start in starlette response classes, so my idea `to attach BackgroundTasks instance to app` sounds stupid. Sorry for it. So, in general, I just need to start all background tasks again manually. – Dmitriy Lunev Apr 25 '23 at 17:56
  • Can't you just run the tasks on startup with the `@on_event(startup)` ? Haven't tried it yet, but should work since it is used for establishing the database connection – lsabi Apr 25 '23 at 20:31
  • @lsabi I've tried it already, and there is a problem with run async `BackgroundTasks` __call__ method in sync `on_event('startup')`. I've already fixed it by using `apscheduler` module, so I'll answer my own question soon. Thank you for your questions, which helped me find a solution – Dmitriy Lunev Apr 26 '23 at 08:23

1 Answers1

0

Thanks @lsabi for questions. With them I figured out, that BackgroundTasks instance not attachable to app instance. It only worked in routes, because of it mechanism: starlette runs such tasks after response (src).

So, I've done the following things:

  1. Used apscheduler module with AsyncIOScheduler to manage tasks.
  2. Still used BackgroundTasks in routes to create tasks in response, and not in route method body.

on events are the following:

@mp_task_router.on_event('startup')
def start_reminder_jobs():
    fcm = FCM()  # push notification class
    with Session() as session:
        reminders_to_notify = ...  # session query
        for reminder in filter(is_reminder_notifiable, reminders_to_notify):
            if not (next_date := reminder.next_date):
                continue
            pet_name: str = ...  # scalar session query
            scheduler.add_job(
                background_reminder_notify,
                'date',
                run_date=next_date - relativedelta(seconds=3),  # dateutil.relativedelta class
                kwargs=dict(
                    fcm=fcm,
                    next_date=next_date,
                    reminder=reminder,
                    pet_name=pet_name,
                ),
            )
    scheduler.start()


@mp_task_router.on_event('shutdown')
def stop_scheduler():
    scheduler.remove_all_jobs()
    scheduler.shutdown(wait=False)

example of using BackgroundTasks in routes with apscheduler:

@mp_task_router.get('', ...)
def example(background_tasks: BackgroundTasks):
    # get all needed data.
    ...
    background_tasks.add_task(
        scheduler.add_job,
        background_reminder_notify,
        'date',
        run_date=reminder.next_date - relativedelta(seconds=5),
        kwargs=dict(
             fcm=fcm,
             next_date=next_date,
             reminder=reminder,
             pet_name=pet_name,
        ),
    )
    return {'detail': 'example'}
Dmitriy Lunev
  • 177
  • 2
  • 12