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
- Fermer toutes les sessions
- 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
.