PostgreSQL
 sql >> Base de données >  >> RDS >> PostgreSQL

Problèmes de connexion avec SQLAlchemy et plusieurs processus

Citant "Comment utiliser les moteurs/connexions/sessions avec le multitraitement Python, ou os.fork()?" avec une emphase supplémentaire :

L'objet SQLAlchemy Engine fait référence à un pool de connexions de connexions de base de données existantes. Ainsi, lorsque cet objet est répliqué sur un processus enfant, l'objectif est de s'assurer qu'aucune connexion à la base de données n'est reportée .

et

Cependant, dans le cas d'une session ou d'une connexion active en transaction partagée, il n'y a pas de solution automatique pour cela ; une application doit s'assurer qu'un nouveau processus enfant n'initie que de nouveaux objets et transactions de connexion, ainsi que des objets de session ORM.

Le problème provient du processus enfant dérivé qui hérite de la session globale en direct , qui s'accroche à une Connection . Quand target appelle init , il écrase les références globales à engine et session , diminuant ainsi leurs refcounts à 0 chez l'enfant, les forçant à finaliser. Si, par exemple, vous créez d'une manière ou d'une autre une autre référence à la session héritée dans l'enfant, vous empêchez son nettoyage - mais ne le faites pas. Après main a rejoint et retourne aux affaires comme d'habitude, il essaie d'utiliser la connexion désormais potentiellement finalisée - ou autrement désynchronisée. Quant à savoir pourquoi cela provoque une erreur seulement après un certain nombre d'itérations, je ne suis pas sûr.

La seule façon de gérer cette situation en utilisant les globales comme vous le faites est de

  1. Fermer toutes les sessions
  2. Appelez engine.dispose()

avant de bifurquer. Cela empêchera les connexions de fuir vers l'enfant. Par exemple :

def main():
    global session
    init()
    try:
        dummy = Dummy(value=1)
        session.add(dummy)
        session.commit()
        dummy_id = dummy.id
        # Return the Connection to the pool
        session.close()
        # Dispose of it!
        engine.dispose()
        # ...or call your cleanup() function, which does the same
        p = multiprocessing.Process(target=target, args=(dummy_id,))
        p.start()
        p.join()
        # Start a new session
        session = Session()
        dummy = session.query(Dummy).get(dummy_id)
        assert dummy.value == 2
    finally:
        cleanup()

Votre deuxième exemple ne déclenche pas la finalisation dans l'enfant, et il semble donc seulement fonctionner, bien qu'il puisse être aussi cassé que le premier, car il hérite toujours d'une copie de la session et de sa connexion définie localement dans main .