[英]How to get around Flask-SQLAlchemy database session timing out (avoiding "MySQL server has gone away")
我在這里多次看到這個問題,但答案對我沒有幫助。
根據我的理解,Flask-SQLAlchemy 在 Flask 請求開始時連接到數據庫,並在完成后拆除此會話。 但是如果這個請求需要很長時間呢?
例如,我的應用程序接受一個 POST 請求,該請求啟動一個需要寫入數據庫的長時間運行的 Celery 任務。 MySQL 服務器在一段時間后“消失”,使得在任務結束時寫回數據庫不起作用。
我在線閱讀以設置options["pool_pre_ping"] = True
以緩解此類問題(使用此處找到的子類化 SQLAlchemy 解決方法),但這沒有區別。 發生了同樣的錯誤,幾乎似乎沒有任何設置實際發生變化。
我認為解決方案是重新考慮任務如何寫入數據庫。 目前,我正在使用來自任務請求的相同session
。 如果這是錯誤的有什么想法嗎?
我認為您對重新思考任務如何寫入數據庫有正確的想法。
讓 Flask 路由將任務附加到隊列可能是謹慎的。 一旦任務進入隊列,Flask 路由就可以返回,因此 Web 請求上下文將被關閉。
然后,您可以在后台運行另一個 python(非燒瓶)程序,該程序不斷從該隊列中讀取,並從中彈出最新的任務,然后執行這些任務並將它們保存到數據庫中。
這可以通過使用來完成 也許您已經在使用 Celery 來調用異步函數? 這是一篇關於如何創建這樣的 Celery 任務並將其與 Flask 路由一起使用的博客文章https://blog.miguelgrinberg.com/post/using-celery-with-flask
您是對的,當 Web 請求上下文關閉時,Flask-SQLAlchemy 會拆除數據庫會話,這在 Flask 路由返回時發生。
為了防止燒瓶的SQLAlchemy從關閉會話不應傳遞您的session
從燒瓶路線異步芹菜功能。 相反,您應該在 celery 異步函數中“創建一個新會話”(調用會話注冊表)。
事實上,這是在 Web 應用程序中使用來自 SQLAlchemy 的會話時的一般原則。 您想避免傳遞會話對象。 SQLAlchemy 通過使用注冊表模式創建一個返回“線程本地”會話對象的會話注冊表來提升這個主體。 “線程本地”會話的這種想法意味着當您從會話注冊表中獲取會話時,您確保該會話僅在當前線程中使用。 由於您最終要創建一個新線程來執行長時間運行的任務,因此您可以依靠 Session Registry 為您提供一個不會受到 Web 請求關閉影響的線程,因為 Web 請求是在不同的線程中處理的.
Flask-SQLAlchemy 默認實現了這個會話注冊表。 例如,您可能有一個路由和芹菜任務,如下所示:
from flask import Flask, request, jsonify
from flask_sqlalchemy import SQLAlchemy
from celery import Celery
app = Flask(__name__)
db = SQLAlchemy(app)
app.config['CELERY_BROKER_URL'] = 'redis://localhost:6379/0'
app.config['CELERY_RESULT_BACKEND'] = 'redis://localhost:6379/0'
celery = Celery(app.name, broker=app.config['CELERY_BROKER_URL'])
celery.conf.update(app.config)
@celery.task
def do_stuff_that_takes_10_minutes(payload):
session = db.session # Gets a thread local session from the Session Registry that is different from the session obj you used in the route because this function will be executed in its own thread.
# some long running task here
return result
@app.route('/long_running_task', methods=['GET'])
def long_running_task():
session = db.session # Get a thread local session from the Session Registry that will close once this web request is complete
# use this session here and do stuff
# Call this function, which Celery will spawn a new thread to do
# Don't pass the above session object into this function, we want this
# session obj to stay only within this thread.
do_stuff_that_takes_10_minutes.delay()
return "Executing task, could take 10 minutes or more."
關閉會話但保留對象。 編輯它們。 然后使用merge
。 確保設置expire_on_commit
為False
。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.