Зависание сценария Python с использованием SQLAlchemy и многопроцессорности

Рассмотрим следующий сценарий Python, который использует SQLAlchemy и модуль многопроцессорности Python. Это с Python 2.6.6-8 + b1 (по умолчанию) и SQLAlchemy 0.6.3-3 (по умолчанию) в сжатии Debian. Это упрощенная версия некоторого реального кода.

import multiprocessing
from sqlalchemy import *
from sqlalchemy.orm import *
dbuser = ...
password = ...
dbname = ...
dbstring = "postgresql://%s:%s@localhost:5432/%s"%(dbuser, password, dbname)
db = create_engine(dbstring)
m = MetaData(db)

def make_foo(i):
    t1 = Table('foo%s'%i, m, Column('a', Integer, primary_key=True))

conn = db.connect()
for i in range(10):
    conn.execute("DROP TABLE IF EXISTS foo%s"%i)
conn.close()
db.dispose()

for i in range(10):
    make_foo(i)

m.create_all()

def do(kwargs):
    i, dbstring = kwargs['i'], kwargs['dbstring']

    db = create_engine(dbstring)
    Session = scoped_session(sessionmaker())
    Session.configure(bind=db)
    Session.execute("COMMIT; BEGIN; TRUNCATE foo%s; COMMIT;")
    Session.commit()
    db.dispose()

pool = multiprocessing.Pool(processes=5)               # start 4 worker processes
results = []
arglist = []
for i in range(10):
    arglist.append({'i':i, 'dbstring':dbstring})
r = pool.map_async(do, arglist, callback=results.append) # evaluate "f(10)" asynchronously
r.get()
r.wait()
pool.close()
pool.join()

Этот сценарий зависает со следующим сообщением об ошибке.

Exception in thread Thread-2:
Traceback (most recent call last):
  File "/usr/lib/python2.6/threading.py", line 532, in __bootstrap_inner
    self.run()
  File "/usr/lib/python2.6/threading.py", line 484, in run
    self.__target(*self.__args, **self.__kwargs)
  File "/usr/lib/python2.6/multiprocessing/pool.py", line 259, in _handle_results
    task = get()
TypeError: ('__init__() takes at least 4 arguments (2 given)', , ('(ProgrammingError) syntax error at or near "%"\nLINE 1: COMMIT; BEGIN; TRUNCATE foo%s; COMMIT;\n        ^\n',))

Конечно, здесь синтаксическая ошибка TRUNCATE foo% s; . У меня вопрос: почему процесс зависает, и могу ли я убедить его вместо этого выйти с ошибкой, не делая серьезных изменений в моем коде? Это поведение очень похоже на мой реальный код.

Обратите внимание, что зависание не происходит, если оператор заменяется чем-то вроде print foobarbaz .Кроме того, зависание все равно произойдет, если мы заменим

Session.execute("COMMIT; BEGIN; TRUNCATE foo%s; COMMIT;")
Session.commit()
db.dispose()

просто Session.execute ("TRUNCATE foo% s;")

Я использую предыдущую версию, потому что она ближе к тому, что делает мой реальный код. .

Кроме того, удаление multiprocessing из изображения и последовательное выполнение цикла по таблицам устраняет зависание, и он просто завершается с ошибкой.

Я также немного озадачен формой ошибки, особенно TypeError: ('__init __ () принимает не менее 4 аргументов (2 задано)' бит. Откуда эта ошибка ? Кажется вероятным, что это откуда-то из кода multiprocessing .

Журналы PostgreSQL бесполезны. Я вижу много строк вроде

2012-01-09 14:16:34.174 IST [7810] 4f0aa96a.1e82/1 12/583 0 ERROR:  syntax error at or near "%" at character 28
2012-01-09 14:16:34.175 IST [7810] 4f0aa96a.1e82/2 12/583 0 STATEMENT:  COMMIT; BEGIN; TRUNCATE foo%s; COMMIT;

, но ничего другого, что кажется актуальным.

ОБНОВЛЕНИЕ 1: Благодаря lbolla и его проницательному анализу я смог подать отчет об ошибке Python об этом. См. Анализ sbt в этом отчете, а также здесь . См. Также отчет об ошибке Python Исправление исключения исключений . Итак, следуя объяснениям sbt, мы можем воспроизвести исходную ошибку с помощью

import sqlalchemy.exc
e = sqlalchemy.exc.ProgrammingError("", {}, None)
type(e)(*e.args)

, что дает

Traceback (most recent call last):
  File "", line 9, in 
TypeError: __init__() takes at least 4 arguments (2 given)

ОБНОВЛЕНИЕ 2: Это было исправлено, по крайней мере для SQLAlchemy, Майком Байером, см. Отчет об ошибке StatementError Исключения не выбираются . . По предложению Майка, я также сообщил об аналогичной ошибке в psycopg2, хотя у меня не было (и нет) реального примера поломки. Тем не менее, они, по-видимому, исправили это, хотя не сообщили подробностей об исправлении. См. исключения psycopg не могут быть обработаны .Для удобства я также сообщил об ошибке Python . Исключения ConfigParser не могут быть обработаны , что соответствует вопросу SO, упомянутому lbolla . Похоже, они хотят проверить это.

В любом случае, похоже, что в обозримом будущем проблема будет продолжаться, поскольку, по большому счету, разработчики Python, похоже, не знают об этой проблеме и поэтому не остерегайтесь ее. Удивительно, но кажется, что недостаточно людей, использующих многопроцессорность, чтобы это стало широко известной проблемой, или, может быть, они просто смирились с этим. Я надеюсь, что разработчики Python найдут способ исправить это по крайней мере для Python 3, потому что это раздражает.

Я принял ответ Иболлы, поскольку без его объяснения того, как проблема связана с обработкой исключений, я бы, вероятно, никуда не пошел бы в понимании этого. Я также хочу поблагодарить sbt, который объяснил, что проблема заключается в том, что Python не может обрабатывать исключения. Я очень благодарен им обоим и, пожалуйста, проголосуйте за их ответы. Спасибо.

ОБНОВЛЕНИЕ 3: Я отправил следующий вопрос: Перехват невыбираемых исключений и повторное повышение .

7
задан Community 23 May 2017 в 12:01
поделиться