У меня возникают некоторые странные ошибки, которые, как представляется, вызваны соединениями, используемыми Sqlalchemy, которые я точно не могу точно определить. Я надеялся, что кто-то подскажет, что происходит здесь.Ошибки соединения SQLAlchemy
Мы работаем над пирамидой (версия 1.5b1) и используем Sqlalchemy (версия 0.9.6) для всех наших подключений к базе данных. Иногда мы получаем ошибки, связанные с подключением дб или сессии, большую часть времени это будет ошибка cursor already closed
или This Connection is closed
, но мы получаем другие связанные исключения тоже:
(OperationalError) connection pointer is NULL
(InterfaceError) cursor already closed
Parent instance <...> is not bound to a Session, and no contextual session is established; lazy load operation of attribute '...' cannot proceed
A conflicting state is already present in the identity map for key (<class '...'>, (1001L,))
This Connection is closed (original cause: ResourceClosedError: This Connection is closed)
(InterfaceError) cursor already closed
Parent instance <...> is not bound to a Session; lazy load operation of attribute '...' cannot proceed
Parent instance <...> is not bound to a Session, and no contextual session is established; lazy load operation of attribute '...' cannot proceed
'NoneType' object has no attribute 'twophase'
(OperationalError) connection pointer is NULL
This session is in 'prepared' state; no further
Там нет серебряной пули, чтобы воспроизвести их, только освежая много раз, они обязательно должны произойти в какой-то момент. Поэтому я создал сценарий с использованием multi-mechanize для одновременного спама разных URL-адресов и просмотра того, где и когда это происходит.
Похоже, что инициированный url не имеет значения, ошибки возникают, когда есть параллельные запросы, которые охватывают более длительное время (и другие запросы обслуживаются между ними). Это, по-видимому, указывает на какую-то проблему резьбонарезания; что либо сеанс, либо соединение разделяются между различными потоками.
После прибегая к помощи этим вопросам, я нашел много тем, большинство из них говорит, чтобы использовать контекстные сеансы, но вещь мы используем их уже:
db_session = scoped_session(sessionmaker(extension=ZopeTransactionExtension(), autocommit=False, autoflush=False))
db_meta = MetaData()
У нас есть BaseModel для все наши объекты ORM:
BaseModel = declarative_base (ЦБС = BaseModelObj, метаклассом = BaseMeta, метаданные = db_meta)
Мы используем pyramid_tm анимации для обработки транзакции во время запроса
Мы подключаем db_session.remove() к событию NewResponse пирамиды (которое запускается после того, как все было запущено). Я также попытался поместить его в отдельную анимацию под управлением pyramid_tm или даже не делать этого вообще, ни один из них, похоже, не имеет эффекта, поэтому ответное событие показалось самым чистым местом для его размещения.
Мы создаем двигатель в нашей главной точке входа в проект нашей пирамиды и используем NullPool и оставляем пул соединений для pgbouncer. Мы также настроить сеанс и привязку для нашего BaseModel здесь: ('SQLAlchemy' config.registry.settings,, poolclass = NullPool)
двигателя = engine_from_config db_session.configure (связывать = двигатель, query_cls = FilterQuery) BaseModel.metadata.bind = двигатель config.add_subscriber (cleanup_db_session, NewResponse) возвращение config.make_wsgi_app()
В нашем приложении мы достигаем все операции дб, используя:
из project.db импорта db_session . .. db_sessi on.query (MyModel) .filter (...) db_session.execute (...)
Мы используем psycopg2 == 2.5.2 для обработки соединения с Postgres с pgbouncer между ними
Я сделал, что никакие ссылки не db_session или соединения сохраняются в любом месте (что может привести к другим потокам их повторного использования)
Я также попытался спам тестирование с использованием разных веб-серверов, использование официантки и cogen я получил ошибки очень легко, используя wsgiref, у нас, несомненно, нет ошибок (которые являются однопоточными). Используя uwsgi и gunicorn (4 рабочих, gevent), я не получил никаких ошибок.
Учитывая различия в используемом веб-сервере, я думал, что это связано с некоторыми веб-серверами, обрабатывающими запросы в потоках, а некоторые - с использованием новых процессов (может быть, проблема с forking)? Чтобы еще больше усложнить ситуацию, когда время продолжалось, и я сделал несколько новых тестов, проблема ушла в официантку, но теперь это произошло с помощью пушки (при использовании gevent)! Я не знаю, как отлаживать это ...
Наконец, чтобы проверить, что происходит с соединением, я привязал атрибут к соединению в начале выполнения курсора и попытался прочитать атрибут на конец выполнения:
@event.listens_for(Engine, "before_cursor_execute")
def _before_cursor_execute(conn, cursor, stmt, params, context, execmany):
conn.pdtb_start_timer = time.time()
@event.listens_for(Engine, "after_cursor_execute")
def _after_cursor_execute(conn, cursor, stmt, params, context, execmany):
print conn.pdtb_start_timer
Удивительно это иногда вызывает исключение: «Connection» объект не имеет атрибута «pdtb_start_timer»
поразивший меня как очень странно .. я нашел одну дискуссию о чем-то подобным: https://groups.google.com/d/msg/sqlalchemy/GQZSjHAGkWM/rDflJvuyWnEJ И попытался добавить стратегию = 'threadlocal' к движку, который fr om, что я понимаю, должно заставить 1 соединение для протектора. Но это не повлияло на ошибки, которые я вижу .. (кроме некоторых сбоев, которые не срабатывают, потому что мне нужны два разных сеанса/соединения для некоторых тестов, и это заставляет связать 1 соединение)
Кто-нибудь знает, что может идти здесь или есть еще несколько указателей на то, как атаковать эту проблему?
Заранее благодарен!
Matthijs Блаас
У меня есть обновление по этой проблеме, теперь я последовательно получаю курсор (InterfaceError) уже закрытым «исключениями * только *» с использованием gunicorn в режиме gentent: https://groups.google.com/d/msg/sqlalchemy/7szX4cbw2Ho/qBHcxYAfDgcJ –