使用sqlalchemy和postgres的SSL系统调用错误文件描述符

时间:2014-03-16 10:35:13

标签: python postgresql ssl sqlalchemy system-calls

所以我有一个守护进程通过sqlalchemy与Postgres对话。守护进程做了这样的事情:

while True:
    oEngine = setup_new_engine()
    with oEngine.connect() as conn:
        Logger.debug("connection established")
        DBSession = sessionmaker(bind=conn)()
        Logger.debug('DBSession created. id={0}'.format(id(DBSession)))

        #do a bunch of stuff with DBSession

        DBSession.commit()
        Logger.debug('DBSession committed. id={0}'.format(id(DBSession)))

在永久循环的第一次迭代中,一切都很好。一阵子。 DBSession成功地向数据库发出了一些查询。但是一个查询失败并出现错误:

OperationalError: (OperationalError) SSL SYSCALL error: Bad file descriptor

这告诉我使用了一个封闭的连接或文件描述符。但这些连接是由守护进程创建和维护的,所以我不知道这意味着什么。

换句话说,会发生什么:

 create engine
 open connection
 setup dbsession
 query dbsession => works great
 query dbsession => ERROR

有问题的查询如下:

 DBSession.query(Login)
                        .filter(Login.LFTime == oLineTime)
                        .filter(Login.success == self.success)
                        .count()

这对我来说似乎完全合情合理。

我的问题是:这种行为可能有什么样的原因,我该如何解决或隔离问题呢?

如果您需要更多代码,请与我们联系。有很多,所以我在这里采取极简主义的方法......

2 个答案:

答案 0 :(得分:2)

我通过考虑会话范围而不是事务范围来解决这个问题。

while True:
    do_stuff()

def do_stuff():
    oEngine = setup_new_engine()
    with oEngine.connect() as conn:
        Logger.debug("connection established")
        DBSession = sessionmaker(bind=conn)()

        #do a bunch of stuff with DBSession

        DBSession.commit()
        DBSession.close()

我仍然想知道为什么这个固定的东西虽然......

答案 1 :(得分:1)

您正在while循环中创建会话,这是非常不明智的。使用第一次使用代码的方式,您将在每次迭代时生成一个新连接并保持打开状态。不久之后,您将受到某种限制并且无法打开另一个新会话。 (什么样的限制?很难说,但它可能是一个内存条件,因为数据库连接非常重;它可能是一个数据库服务器限制,它只会因性能原因接受一定数量的同时用户连接;很难知道并且它并不重要,因为无论限制是什么,它都会阻止您使用非常浪费的方法,因此已按预期工作!)

您遇到的解决方案可以解决问题,因为当您为每个循环打开一个新连接时,您也可以使用每个循环关闭它,释放资源并允许其他循环创建自己的会话并成功。但是,这仍然是服务器和客户端上的大量不必要的繁忙和浪费处理资源。如果你将sessionmaker移到while循环之外,我怀疑它会同样有效 - 并且可能很多更快。

def main():
    oEngine = setup_new_engine()
    with oEngine.connect() as conn:
        Logger.debug("connection established")
        DBSession = sessionmaker(bind=conn)()

        apparently_infinite_loop(DBSession)

        # close only after we are done and have somehow exited the infinite loop
        DBSession.close()


def apparently_infinite_loop(DBSession):
    while True:

        #do a bunch of stuff with DBSession

        DBSession.commit()

我目前没有工作的sqlalchemy设置,所以你可能会有一些语法错误,但无论如何我希望它能说明基本的根本问题。

此处提供了更多详细信息:http://docs.sqlalchemy.org/en/rel_0_9/orm/session.html#session-faq-whentocreate

文档中的一些要点:

  1. “如果再次使用,会话将开始新的交易”。所以这就是为什么你不需要经常打开新的会话以获得交易范围;只需提交即可。
  2. “作为一般规则,应用程序应该将会话外部的生命周期管理到处理特定数据的函数。”因此,您最初(现在仍然)的基本问题就是所有会话管理都在您的数据处理代码旁边的while循环中继续进行。