这个线程局部的Flask-SQLAchemy会话导致“MySQL服务器已经消失”错误吗?

时间:2016-01-08 19:25:01

标签: python mysql sqlalchemy

我有一个Web应用程序,它运行独立于用户会话的长作业。为实现这一点,我有一个线程局部Flask-SQLAlchemy会话的实现。问题是一天几次,当我访问我的网站时出现MySQL server has gone away错误。该网站始终在刷新时加载。我认为这个问题与这些线程本地会话有关,但我不确定。

这是我对线程局部会话范围的实现:

@contextmanager
def thread_local_session_scope():
    """Provides a transactional scope around a series of operations.
    Context is local to current thread.
    """
    # See this StackOverflow answer for details:
    # http://stackoverflow.com/a/18265238/1830334
    Session = scoped_session(session_factory)
    threaded_session = Session()
    try:
        yield threaded_session
        threaded_session.commit()
    except:
        threaded_session.rollback()
        raise
    finally:
        Session.remove()

这是我的标准Flask-SQLAlchemy会话:

@contextmanager
def session_scope():
    """Provides a transactional scope around a series of operations.
    Context is HTTP request thread using Flask-SQLAlchemy.
    """
    try:
        yield db.session
        db.session.commit()
    except Exception as e:
        print 'Rolling back database'
        print e
        db.session.rollback()
    # Flask-SQLAlchemy handles closing the session after the HTTP request.

然后我使用这两个会话上下文管理器:

def build_report(tag):
    report = _save_report(Report())
    thread = Thread(target=_build_report, args=(report.id,))
    thread.daemon = True
    thread.start()
    return report.id

# This executes in the main thread.
def _save_report(report):
    with session_scope() as session:
        session.add(report)
        session.commit()
        return report

# These executes in a separate thread.
def _build_report(report_id):
    with thread_local_session_scope() as session:
        report = do_some_stuff(report_id)
        session.merge(report)

编辑:引擎配置

app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql://<username>:<password>@<server>:3306/<db>?charset=utf8'
app.config['SQLALCHEMY_POOL_RECYCLE'] = 3600
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False

1 个答案:

答案 0 :(得分:1)

尝试添加

app.teardown_request(Exception=None)

装饰器,在每个请求结束时执行。我目前遇到类似的问题,似乎今天我实际上已经解决了它。

@app.teardown_request
def teardown_request(exception=None):
    Session.remove()
    if exception and Session.is_active:
        print(exception)
        Session.rollback()

我不使用Flask-SQLAlchemy仅限原始SQLAlchemy,因此它可能会有所不同。

来自文档

  

拆解回调是特殊的回调,因为它们被执行   在不同的点。严格来说,他们是独立的   实际请求处理,因为它们绑定到生命周期   RequestContext对象。弹出请求上下文时,   teardown_request()函数被调用。

在我的情况下,我为每个请求打开一个新的scoped_session,要求我在每个请求结束时删除它(Flask-SQLAlchemy可能不需要这个)。此外,如果在上下文期间发生teardown_request函数,则传递te Exception。在这种情况下,如果发生异常(可能导致事务未被删除或需要回滚),我们会检查是否存在异常并进行回滚。

如果这对我自己的测试不起作用,我接下来要做的就是在每次拆解时session.commit(),以确保一切都在冲洗

更新:看来MySQL也会在8小时后使连接失效,导致会话损坏。

在您的引擎配置或设置&lt;上设置pool_recycle=3600 MySQL超时。这与正确的会话范围(结束会话)相结合应该这样做。