MySQL服务器已经消失 - 通过checkout事件处理程序断开连接处理不起作用

ron*_*ddd 8 python sqlalchemy multiprocessing mysql-python python-elixir

更新3/4:

我做了一些测试并证明使用checkout事件处理程序检查断开连接是否与Elixir一起使用.开始认为我的问题与session.commit()从子进程调用有关?更新:我只是通过调用session.commit()下面的子进程,更新示例来反驳自己.我正在使用多处理模块来创建子流程.

这是代码,显示它应该如何工作(甚至没有使用pool_recycle!):

from sqlalchemy import exc
from sqlalchemy import event
from sqlalchemy.pool import Pool
from elixir import *
import multiprocessing as mp

class SubProcess(mp.Process):
    def run(self):
        a3 = TestModel(name="monkey")
        session.commit()

class TestModel(Entity):
    name = Field(String(255))

@event.listens_for(Pool, "checkout")
def ping_connection(dbapi_connection, connection_record, connection_proxy):
    cursor = dbapi_connection.cursor()
    try:
        cursor.execute("SELECT 1")
    except:
        # optional - dispose the whole pool
        # instead of invalidating one at a time
        # connection_proxy._pool.dispose()

        # raise DisconnectionError - pool will try
        # connecting again up to three times before raising.
        raise exc.DisconnectionError()
    cursor.close()

from sqlalchemy import create_engine
metadata.bind = create_engine("mysql://foo:bar@localhost/some_db", echo_pool=True)
setup_all(True)

subP = SubProcess()

a1 = TestModel(name='foo')
session.commit()

# pool size is now three.

print "Restart the server"
raw_input()

subP.start()

#a2 = TestModel(name='bar')
#session.commit()
Run Code Online (Sandbox Code Playgroud)

更新2:

我不得不找到另一个解决方案,因为1.2.2版本的MySQL-python支持重新连接参数.有人有解决方案吗?:\

更新1(旧解决方案,不适用于MySQL-python版本> 1.2.2):

找到解决方案:connect_args={'reconnect':True}转到create_engine调用修复问题,自动重新连接.甚至似乎不需要checkout事件处理程序.

所以,在问题的例子中:

metadata.bind = create_engine("mysql://foo:bar@localhost/db_name", pool_size=100, pool_recycle=3600, connect_args={'reconnect':True})
Run Code Online (Sandbox Code Playgroud)

原始问题:

为此问题做了相当多的谷歌搜索,似乎没有找到特定于Elixir的解决方案 - 我正在尝试使用SQLAlchemy文档中的" Disconnect Handling - Pessimistic "示例来处理MySQL断开连接.但是,当我测试它(通过重新启动MySQL服务器)时,"MySQL服务器已经消失"错误在我的结帐事件处理程序之前引发.

这是我用来初始化elixir的代码:

##### Initialize elixir/SQLAlchemy
# Disconnect handling
from sqlalchemy import exc
from sqlalchemy import event
from sqlalchemy.pool import Pool

@event.listens_for(Pool, "checkout")
def ping_connection(dbapi_connection, connection_record, connection_proxy):
    logging.debug("***********ping_connection**************")
    cursor = dbapi_connection.cursor()
    try:
        cursor.execute("SELECT 1")
    except:
        logging.debug("######## DISCONNECTION ERROR #########")            
        # optional - dispose the whole pool
        # instead of invalidating one at a time
        # connection_proxy._pool.dispose()

        # raise DisconnectionError - pool will try
        # connecting again up to three times before raising.
        raise exc.DisconnectionError()
    cursor.close()

metadata.bind= create_engine("mysql://foo:bar@localhost/db_name", pool_size=100, pool_recycle=3600)

setup_all()
Run Code Online (Sandbox Code Playgroud)

我创建了elixir实体对象并保存它们session.commit(),在此期间我看到从上面定义的事件生成的"ping_connection"消息.但是,当我重新启动mysql服务器并再次测试它时,它失败,而mysql服务器已经在ping连接事件之前消失了.

这是从相关行开始的堆栈跟踪:

  File "/usr/local/lib/python2.6/dist-packages/elixir/entity.py", line 1135, in get_by
    return cls.query.filter_by(*args, **kwargs).first()
  File "/usr/local/lib/python2.6/dist-packages/sqlalchemy/orm/query.py", line 1963, in first
    ret = list(self[0:1])
  File "/usr/local/lib/python2.6/dist-packages/sqlalchemy/orm/query.py", line 1857, in __getitem__
    return list(res)
  File "/usr/local/lib/python2.6/dist-packages/sqlalchemy/orm/query.py", line 2032, in __iter__
    return self._execute_and_instances(context)
  File "/usr/local/lib/python2.6/dist-packages/sqlalchemy/orm/query.py", line 2047, in _execute_and_instances
    result = conn.execute(querycontext.statement, self._params)
  File "/usr/local/lib/python2.6/dist-packages/sqlalchemy/engine/base.py", line 1399, in execute
    params)
  File "/usr/local/lib/python2.6/dist-packages/sqlalchemy/engine/base.py", line 1532, in _execute_clauseelement
    compiled_sql, distilled_params
  File "/usr/local/lib/python2.6/dist-packages/sqlalchemy/engine/base.py", line 1640, in _execute_context
    context)
  File "/usr/local/lib/python2.6/dist-packages/sqlalchemy/engine/base.py", line 1633, in _execute_context
    context)
  File "/usr/local/lib/python2.6/dist-packages/sqlalchemy/engine/default.py", line 330, in do_execute
    cursor.execute(statement, parameters)
  File "/usr/lib/pymodules/python2.6/MySQLdb/cursors.py", line 166, in execute
    self.errorhandler(self, exc, value)
  File "/usr/lib/pymodules/python2.6/MySQLdb/connections.py", line 35, in defaulterrorhandler
    raise errorclass, errorvalue
OperationalError: (OperationalError) (2006, 'MySQL server has gone away') 
Run Code Online (Sandbox Code Playgroud)

ron*_*ddd 2

最后的解决方法是session.remove()在操作和加载 Elixir 实体之前调用方法的开头。它的作用是将连接返回到池中,这样当再次使用它时,池的签出事件将被触发,并且我们的处理程序将检测到断开连接。来自 SQLAlchemy 文档

\n\n
\n

在请求结束时删除会话并不是绝对必要的 - 其他选项包括在最后调用 Session.close()、Session.rollback()、Session.commit() 以便现有会话将其连接返回到池并删除任何现有的事务上下文。如果各个控制器方法负责确保请求结束后没有事务保持打开状态,那么什么也不做也是一种选择。

\n
\n\n

这是一个相当重要的小信息,我希望在 Elixir 文档中提到它。但我想它假设先有 SQLAlchemy 知识?

\n