gwg*_*gwg 5 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()
Run Code Online (Sandbox Code Playgroud)
这是我的标准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.
Run Code Online (Sandbox Code Playgroud)
然后我使用这两个会话上下文管理器:
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)
Run Code Online (Sandbox Code Playgroud)
编辑:引擎配置
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
Run Code Online (Sandbox Code Playgroud)
尝试添加一个
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()
Run Code Online (Sandbox Code Playgroud)
我不使用Flask-SQLAlchemyOnly Raw SQLAlchemy,所以它对你来说可能会有所不同。
来自文档
拆卸回调是特殊的回调,因为它们在不同的点执行。严格来说,它们独立于实际的请求处理,因为它们绑定到 RequestContext 对象的生命周期。当请求上下文被弹出时,teardown_request()函数被调用。
就我而言,我scoped_session为每个请求打开一个新的,要求我在每个请求结束时将其删除(Flask-SQLAlchemy可能不需要这个)。此外,teardown_request 函数会被传递(Exception如果在上下文期间发生)。在这种场景下,如果发生异常(可能导致事务没有被移除,或者需要回滚),我们检查是否有异常,并回滚。
如果这对我自己的测试不起作用,那么我接下来要做的就是session.commit()每次拆卸时,只是为了确保一切都冲洗干净
更新:MySQL 似乎也会在 8 小时后使连接失效,导致会话损坏。
设置pool_recycle=3600您的引擎配置,或设置 < MySQL 超时。这与适当的会话范围(关闭会话)相结合应该可以做到。