2014-09-10 2 views
5

У меня возникают некоторые странные ошибки, которые, как представляется, вызваны соединениями, используемыми 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 Блаас

+0

У меня есть обновление по этой проблеме, теперь я последовательно получаю курсор (InterfaceError) уже закрытым «исключениями * только *» с использованием gunicorn в режиме gentent: https://groups.google.com/d/msg/sqlalchemy/7szX4cbw2Ho/qBHcxYAfDgcJ –

ответ

1

Обновление: Ошибки, где вызванные несколькими командами, где отправить в одном заявлении SQL. Psycopg2, похоже, допускает это, но, по-видимому, это может вызвать странные проблемы. Разъем PG8000 более строгий и выдается на несколько команд, по одной команде исправлена ​​проблема!