Citerar "Hur använder jag motorer / anslutningar / sessioner med Python multiprocessing, eller os.fork()?" med ytterligare betoning:
SQLAlchemy Engine-objektet hänvisar till en anslutningspool med befintliga databasanslutningar. Så när det här objektet replikeras till en underordnad process är målet att säkerställa att inga databasanslutningar överförs .
och
Men för fallet med en transaktionsaktiv session eller anslutning som delas, finns det ingen automatisk fix för detta; en applikation måste säkerställa att en ny underordnad process endast initierar nya Connection-objekt och transaktioner, såväl som ORM-sessionsobjekt.
Problemet härrör från den splittrade underordnade processen som ärver den live globala sessionen
, som håller fast vid en Anslutning
. När mål
anropar init
, skriver den över de globala referenserna till motor
och session
, vilket minskar deras återräkningar till 0 i barnet, vilket tvingar dem att slutföra. Om du till exempel på ett eller annat sätt skapar en annan referens till den ärvda sessionen hos barnet, förhindrar du att det städas upp – men gör inte det. Efter main
har gått med och återgår till verksamheten som vanligt försöker den använda den nu potentiellt slutförda – eller på annat sätt osynkroniserad – anslutning. Varför detta orsakar ett fel först efter ett antal iterationer är jag inte säker på.
Det enda sättet att hantera den här situationen med hjälp av globala som du gör är att
- Stäng alla sessioner
- Ring
engine.dispose()
innan gaffel. Detta kommer att förhindra att anslutningar läcker till barnet. Till exempel:
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()
Ditt andra exempel utlöser inte slutförande i barnet, så det verkar bara fungera, även om det kan vara lika trasigt som det första, eftersom det fortfarande ärver en kopia av sessionen och dess anslutning definierad lokalt i main .