Cytując "Jak korzystać z silników / połączeń / sesji z wieloprocesorowością Pythona lub os.fork()?" z dodatkowym naciskiem:
Obiekt SQLAlchemy Engine odwołuje się do puli połączeń istniejących połączeń z bazą danych. Więc kiedy ten obiekt jest replikowany do procesu podrzędnego, celem jest zapewnienie, że żadne połączenia z bazą danych nie zostaną przeniesione .
i
Jednak w przypadku współdzielenia sesji aktywnej w ramach transakcji lub połączenia, nie ma na to automatycznej naprawy; aplikacja musi zapewnić, że nowy proces potomny inicjuje tylko nowe obiekty i transakcje połączenia, a także obiekty sesji ORM.
Problem wynika z rozwidlonego procesu potomnego dziedziczącego globalną sesję
na żywo , który utrzymuje Połączenie
. Kiedy cel
wywołuje init
, nadpisuje globalne odniesienia do silnika
i sesja
, zmniejszając w ten sposób ich refcounts do 0 u dziecka, zmuszając je do sfinalizowania. Jeśli na przykład w ten czy inny sposób utworzysz inne odniesienie do odziedziczonej sesji w dziecku, zapobiegniesz jej wyczyszczeniu – ale nie rób tego. Po głównym
dołączył i wraca do pracy jak zwykle, próbuje użyć teraz potencjalnie sfinalizowanego – lub w inny sposób niezsynchronizowanego – połączenia. Nie jestem pewien, dlaczego powoduje to błąd dopiero po kilku iteracjach.
Jedynym sposobem poradzenia sobie z tą sytuacją przy użyciu globalnych sposobów jest
- Zamknij wszystkie sesje
- Wywołaj
engine.dispose()
przed rozwidleniem. Zapobiegnie to wyciekaniu połączeń do dziecka. Na przykład:
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()
Twój drugi przykład nie wyzwala finalizacji u dziecka, więc tylko wydaje się działać, chociaż może być tak samo uszkodzony jak pierwszy, ponieważ nadal dziedziczy kopię sesji i jej połączenie zdefiniowane lokalnie w main .