我有一个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
答案 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超时。这与正确的会话范围(结束会话)相结合应该这样做。