Рассмотрим следующий скрипт Python, который использует SQLAlchemy и модуль многопроцессорной обработки Python.
Это с Python 2.6.6-8 + b1 (по умолчанию) и SQLAlchemy 0.6.3-3 (по умолчанию) в Debian squeeze.
Это упрощенная версия некоторого фактического кода.
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)', <class 'sqlalchemy.exc.ProgrammingError'>, ('(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__() takes at least 4 arguments (2 given)'
. Откуда эта ошибка? Кажется вероятным, что это где-то в 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 "<stdin>", line 9, in <module>
TypeError: __init__() takes at least 4 arguments (2 given)
ОБНОВЛЕНИЕ 2: Это исправлено, по крайней мере для SQLAlchemy, Майком Байером, см. Отчет об ошибке Исключения StatementError не выбираются. . По предложению Майка, я также сообщил о подобной ошибке в psycopg2, хотя у меня не было (и нет) фактического примера поломки. Несмотря на это, они, по-видимому, исправили это, хотя они не дали подробностей исправления. См. psycopg исключения не могут быть засолены . В качестве примера, я также сообщил, что ошибка Python Исключения ConfigParser не могут быть изменены , что соответствует , о чем так и упоминается в вопросе . Кажется, они хотят проверить это.
В любом случае, похоже, что в обозримом будущем это будет оставаться проблемой, поскольку, по большому счету, разработчики Python, похоже, не знают об этой проблеме и поэтому не принимают меры против нее. Удивительно, но кажется, что людей, использующих многопроцессорность, недостаточно для того, чтобы это стало хорошо известной проблемой, или, может быть, они просто смирились с этим. Я надеюсь, что разработчики Python смогут исправить это хотя бы для Python 3, потому что это раздражает.
Я принял ответ Иболлы, так как без его объяснения того, как проблема связана с обработкой исключений, я бы, вероятно, никуда не пошел, чтобы понять это. Я также хочу поблагодарить sbt, который объяснил, что проблема заключалась в том, что Python не мог выбрать исключения. Я очень благодарен им обоим, и, пожалуйста, проголосуйте за их ответы. Спасибо.
ОБНОВЛЕНИЕ 3: я опубликовал следующий вопрос: Поймать необратимые исключения и повторно поднять .