Citeren "Hoe gebruik ik engines / verbindingen / sessies met Python multiprocessing, of os.fork()?" met extra nadruk:
Het SQLAlchemy Engine-object verwijst naar een verbindingspool van bestaande databaseverbindingen. Dus wanneer dit object wordt gerepliceerd naar een onderliggend proces, het doel is ervoor te zorgen dat er geen databaseverbindingen worden overgedragen .
en
Voor het geval dat een transactie-actieve sessie of verbinding wordt gedeeld, is hier echter geen automatische oplossing voor; een applicatie moet ervoor zorgen dat een nieuw onderliggend proces alleen nieuwe Connection-objecten en transacties initieert, evenals ORM Session-objecten.
Het probleem komt voort uit het gevorkte kindproces dat de live globale session
overneemt , die vasthoudt aan een Connection
. Wanneer target
roept init
. aan , het overschrijft de globale verwijzingen naar engine
en session
, waardoor hun refcounts bij het kind tot 0 worden verlaagd, waardoor ze gedwongen worden om af te ronden. Als je bijvoorbeeld op de een of andere manier een andere verwijzing naar de geërfde sessie in het kind creëert, voorkom je dat deze wordt opgeruimd - maar doe dat niet. Na main
is toegetreden en keert terug naar de normale gang van zaken, het probeert de nu mogelijk voltooide - of anderszins niet gesynchroniseerde - verbinding te gebruiken. Waarom dit pas na een aantal iteraties een fout veroorzaakt, weet ik niet zeker.
De enige manier om met deze situatie om te gaan met globals zoals u dat doet, is door
- Sluit alle sessies
- Bel
engine.dispose()
voor het vertakken. Zo voorkom je dat er verbindingen naar het kind lekken. Bijvoorbeeld:
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()
Uw tweede voorbeeld activeert de finalisatie niet bij het kind, en dus lijkt het alleen maar te werken, hoewel het net zo kapot kan zijn als het eerste, omdat het nog steeds een kopie van de sessie overneemt en de verbinding die lokaal is gedefinieerd in main
.