我遇到了一些奇怪的错误,这些错误似乎是由 Sqlalchemy 使用的连接引起的,我无法准确确定……我希望有人知道这里发生了什么。
我们正在开发 Pyramid(版本 1.5b1)并使用 Sqlalchemy(版本 0.9.6)进行我们所有的数据库连接。有时我们会收到与数据库连接或 session 相关的错误,大多数情况下这是 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
没有 Elixir 来重现它们,只有通过多次刷新它们必然会在某些时候发生。所以我使用 multi-mechanize 制作了一个脚本来同时向不同的 url 发送垃圾邮件,并查看它发生的地点和时间。
看起来触发的 url 并不重要,当存在跨越较长时间的并发请求(并且其他请求在两者之间提供服务)时会发生错误。这似乎表明存在某种线程问题; session 或连接在不同线程之间共享。
在谷歌搜索这些问题后,我发现了很多主题,其中大多数都告诉使用作用域 session ,但问题是我们已经使用它们了:
db_session = scoped_session(sessionmaker(extension=ZopeTransactionExtension(), autocommit=False, autoflush=False))
db_meta = MetaData()
BaseModel = declarative_base(cls=BaseModelObj, metaclass=BaseMeta, metadata=db_meta)
引擎 = engine_from_config(config.registry.settings, 'sqlalchemy.', poolclass=NullPool)
db_session.configure(绑定(bind)=引擎,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(...)
我还使用不同的网络服务器尝试了垃圾邮件测试,使用女服务员和 cogen 我很容易得到错误,使用 wsgiref 不出所料,我们没有错误(这是单线程的)。使用 uwsgi 和 gunicorn(4 个 worker ,gevent)我没有收到任何错误。
鉴于所使用的网络服务器的差异,我认为这要么与某些网络服务器处理线程中的请求有关,而有些则与使用新进程有关(可能是 fork 问题)?更复杂的是,随着时间的推移,我做了一些新的测试,问题在女服务员身上消失了,但现在发生在 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”对象没有属性“pdtb_start_timer”
这让我觉得很奇怪..我发现了一个关于类似问题的讨论:https://groups.google.com/d/msg/sqlalchemy/GQZSjHAGkWM/rDflJvuyWnEJ
并尝试向引擎添加 strategy='threadlocal',据我所知,这应该强制胎面连接 1 个。但它对我看到的错误没有任何影响..(除了一些单元测试失败,因为我需要两个不同的 session /连接进行一些测试,这会强制关联 1 个连接)
有没有人知道这里可能会发生什么或者有更多关于如何解决这个问题的指示?
提前致谢!
马蒂斯·布拉斯
最佳答案
更新:由在一个准备好的 sql 语句中发送的多个命令引起的错误。 Psycopg2 似乎允许这样做,但显然它会导致奇怪的问题。 PG8000 连接器更严格,并支持多条命令,发送一条命令即可解决问题!
关于python - SQLAlchemy 连接错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25768428/