Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Celery - SQLAlchemy : problème de fermeture de session #3050

Closed
TheoLechemia opened this issue May 16, 2024 · 1 comment
Closed

Celery - SQLAlchemy : problème de fermeture de session #3050

TheoLechemia opened this issue May 16, 2024 · 1 comment
Milestone

Comments

@TheoLechemia
Copy link
Member

TheoLechemia commented May 16, 2024

Version
2.14.0

Description du bug
Les tâches lancées par celery lève l’exception suivante :
RuntimeError: Working outside of application context , ce qui fait planter le module d'export et toutes les tâches asynchrones de GeoNature.
Suite à ce problème de perf : #2717 , on tente de fermer la session à chaque fin de tâche celery ->https://github.com/PnX-SI/GeoNature/blob/master/backend/geonature/tasks/__init__.py#L9
Or quand on est dans une tâche celery on est plus dans "l'app context" de Flask.
Depuis la version 3.0.0 de Flask-SQLAlchemy, on ne peut plus fermer une session hors de l'app context : https://flask-sqlalchemy.palletsprojects.com/en/3.1.x/changes/#version-3-0-5 :

An active Flask application context is always required to access session and engine, regardless of if an application was passed to the constructor. #508, 944

De manière général, l'utilisation de l'objet db de flask-sqlalchemy, n'est pas vraiment compatible avec l'utilisation de celery. La session flask-sqlalchemy étant créée puis fermée lors d'une requête HTTP : quand on crée une tâche celery, on sort du contexte de la requête (et de l'app), donc on ne peut pas fermer correctement la session (d'ou le problème de perf remarqué précédement).
Cet article propose une façon élégante de créer et de passer la session aux tâche celery : https://celery.school/sqlalchemy-session-celery-tasks

ça donnerait quelquechose comme ça dans le fichier geonature.utils.celery.py

class SQLASessionTask(Task):
    def __init__(self):
        self.sessions = {}

    def before_start(self, task_id, args, kwargs):
        engine = create_engine(
            config["SQLALCHEMY_DATABASE_URI"],
        )
        session = Session(bind=engine)
        self.sessions[task_id] = session
        super().before_start(task_id, args, kwargs)

    def after_return(self, status, retval, task_id, args, kwargs, einfo):
        session = self.sessions.pop(task_id)
        session.close()
        super().after_return(status, retval, task_id, args, kwargs, einfo)

    @property
    def session(self):
        return self.sessions[self.request.id]


celery_app = Celery("geonature", task_cls=SQLASessionTask)

les tâches devront ensuite utiliser la session ainsi :

@celery_app.task(bind=True)
def test(self, export_id):
    export = self.session.get(Export, export_id)

Le problème reste maintenant de passer cette session à toute les fonctions utilisées par les tâches (GenericQuery, GenericTables etc etc...

Edit :
Il y a peut être la possibilité de pousser un app_context lors du lancement du worker : https://stackoverflow.com/questions/12044776/how-to-use-flask-sqlalchemy-in-a-celery-task

@jacquesfize
Copy link
Contributor

Les modifications apportées pour résoudre ce problème sont disponibles dans la version 2.14.2 de GeoNature.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants