【问题标题】:SQLAlchemy connection errorsSQLAlchemy 连接错误
【发布时间】:2014-11-04 06:43:11
【问题描述】:

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

我们正在开发一个 Pyramid(版本 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()
  • 我们的所有 orm 对象都有一个 BaseModel:

    BaseModel = declarative_base(cls=BaseModelObj, metaclass=BaseMeta, metadata=db_meta)

  • 我们使用 pyramid_tm tween 在请求期间处理事务

  • 我们将 db_session.remove() 挂钩到金字塔 NewResponse 事件(在一切运行后触发)。我还尝试将它放在一个单独的补间中,在 pyramid_tm 之后运行,甚至根本不这样做,这些似乎都没有效果,所以响应事件似乎是放置它的最干净的地方。

  • 我们在金字塔项目的主入口点创建引擎并使用 NullPool 并将连接池留给 pgbouncer。我们还在此处为 BaseModel 配置会话和绑定:

    engine = engine_from_config(config.registry.settings, 'sqlalchemy.', poolclass=NullPool) db_session.configure(bind=engine, query_cls=FilterQuery) BaseModel.metadata.bind = 引擎 config.add_subscriber(cleanup_db_session,NewResponse) 返回 config.make_wsgi_app()

  • 在我们的应用程序中,我们使用以下方式访问所有数据库操作:

    从 project.db 导入 db_session ... db_session.query(MyModel).filter(...) db_session.execute(...)

  • 我们使用 psycopg2==2.5.2 来处理与 pgbouncer 之间的 postgres 的连接

  • 我确保没有对 db_session 或连接的引用保存在任何地方(这可能导致其他线程重用它们)

我还尝试使用不同的网络服务器进行垃圾邮件测试,使用 waitress 和 cogen 我很容易得到错误,使用 wsgiref 我们不出所料地没有错误(这是单线程的)。使用 uwsgi 和 gunicorn(4 个工人,gevent)我没有得到任何错误。

考虑到所使用的网络服务器的差异,我认为这与一些网络服务器在线程中处理请求和一些使用新进程(可能是分叉问题)有关?更复杂的是,随着时间的推移,我做了一些新的测试,问题在女服务员身上消失了,但现在发生在 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

令人惊讶的是,这有时会引发异常:'Connection' object has no attribute 'pdtb_start_timer'

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

有没有人知道这里可能会发生什么,或者有更多关于如何解决这个问题的指示?

提前致谢!

马蒂斯·布拉斯

【问题讨论】:

标签: python multithreading sqlalchemy


【解决方案1】:

更新:在一个准备好的 sql 语句中发送多个命令导致的错误。 Psycopg2 似乎允许这样做,但显然它会导致奇怪的问题。 PG8000 连接器更严格,对多条命令进行了保释,发送一条命令即可解决问题!

【讨论】:

    猜你喜欢
    • 2017-10-23
    • 1970-01-01
    • 2021-10-11
    • 1970-01-01
    • 2012-07-24
    • 2018-09-23
    • 1970-01-01
    • 2014-07-08
    • 2020-03-31
    相关资源
    最近更新 更多