Цитирайки "Как да използвам двигатели/връзки/сесии с многопроцесорна обработка на Python или os.fork()?" с допълнителен акцент:
Обектът SQLAlchemy Engine се отнася до пул за връзки от съществуващи връзки към база данни. Така че, когато този обект се репликира в дъщерен процес, целта е да се гарантира, че не се пренасят връзки към базата данни .
и
Въпреки това, в случай на споделена сесия или връзка с активна транзакция, няма автоматично решение за това; едно приложение трябва да гарантира, че нов дъщерен процес инициира само нови обекти и транзакции за връзка, както и обекти ORM Session.
Проблемът произтича от разклонения дъщерен процес, наследяващ глобалната сесия
на живо , който държи Връзка
. Когато target
извиква init
, той презаписва глобалните препратки към engine
и сесия
, като по този начин намалява техните refcounts до 0 в детето, принуждавайки ги да финализират. Ако например по един или друг начин създадете друга препратка към наследената сесия в детето, вие предотвратявате изчистването й – но не правете това. След main
се присъедини и се връща към обичайния си начин, той се опитва да използва сега потенциално финализираната – или по друг начин несинхронизирана – връзка. Не съм сигурен защо това причинява грешка само след известно количество повторения.
Единственият начин да се справите с тази ситуация, като използвате глобални по начина, по който го правите, е да
- Затваряне на всички сесии
- Обадете се на
engine.dispose()
преди разклоняване. Това ще предотврати изтичане на връзки към детето. Например:
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()
Вторият ви пример не задейства финализиране в детето и така изглежда само работи, въпреки че може да е също толкова счупен като първия, тъй като все още наследява копие на сесията и нейната връзка, дефинирана локално в mainкод> .