2014-09-10 127 views
5

我遇到了一些奇怪的错误,这些错误似乎是由sqlalchemy使用的连接引起的,我无法准确地确定它。我希望有人知道这里发生了什么。SQLAlchemy连接错误

我们正在研究金字塔(版本1.5b1),并使用Sqlalchemy(版本0.9.6)来实现我们所有的数据库连接。有时我们会涉及到数据库连接或会话的错误,大多数时候这将是一个cursor already closedThis 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 

没有银弹复制它们,只通过多次刷新,它们肯定会在某个时刻发生。所以我制作了一个使用多机械化的脚本来同时发送不同的URL,并查看它发生的时间和位置。

看起来被触发的网址并不重要,当存在跨越较长时间的并发请求时(以及其他请求在两者之间服务),错误发生。这似乎表明存在某种线程问题;会话或连接在不同的线程之间共享。

google搜索了这些问题后,我发现了很多的话题,他们大多告诉使用范围的会议,但事情是我们已经使用它们:

db_session = scoped_session(sessionmaker(extension=ZopeTransactionExtension(), autocommit=False, autoflush=False)) 
db_meta = MetaData() 
  • 我们有一个BaseModel我们所有的ORM对象:

    BaseModel = declarative_base(CLS = 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,以处理

  • 之间我确信没有提及db_session或连接保存任何地方(这可能导致其他线程重复使用它们)与pgbouncer的Postgres连接

我也尝试了垃圾邮件测试使用不同的网络服务器,使用女服务员和cogen我很容易得到错误,使用wsgiref我们毫不奇怪,没有错误(这是单线程)。使用uwsgi和gunicorn(4名工人,gevent)我没有得到任何错误。

鉴于所使用的网络服务器的差异,我认为它要么处理线程中的一些Web服务器处理请求,有的使用新的进程(也许是分叉问题)?更复杂的是,当时间流逝,我做了一些新的测试,问题已经消失在服务员,但现在发生在gunicorn(使用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 

令人惊讶这有时会引发异常:“连接”对象有没有属性“pdtb_start_timer”

这让我觉得很奇怪。我发现了大约类似一个讨论:https://groups.google.com/d/msg/sqlalchemy/GQZSjHAGkWM/rDflJvuyWnEJ 并尝试添加strategy ='threadlocal'到引擎,其中fr嗡嗡我知道应该强制1胎面连接。但它没有影响我看到的错误..(除了一些单位测试失败,因为我需要两个不同的会话/连接进行一些测试,这迫使1个连接关联)

有没有人知道什么是什么可能会继续下去,或者有更多关于如何解决这个问题的指针?

在此先感谢!

Matthijs BLAAS

+0

我有这个问题的更新,我现在一直得到“(InterfaceError)游标已经关闭”例外*仅*在gevent模式下使用gunicorn:https://groups.google.com/d/msg/sqlalchemy/7szX4cbw2Ho/qBHcxYAfDgcJ – 2014-09-11 16:37:30

回答

1

更新:其中由多个命令说,若一个准备SQL语句发送引起的误差。 Psycopg2似乎允许这一点,但显然它可能会导致奇怪的问题。 PG8000连接器在多个命令中更加严格并保留,发送一个命令可解决问题!