Даже если установлен SQLALCHEMY_POOL_RECYCLE, Flask-SQLAlchemy не будет автоматически перерабатывать соединения MySQL

Недавно моя программа Flask обнаружила ошибку Lost connection to MySQL server during query. Я искал некоторые документы и обсуждения. Кажется, что MySQL отключил соединение, но flask не перезапустил эти соединения с истекшим сроком действия.
Сейчас я использую Flask_SQLAlchemy==2.4.4. Из документации flask 2.x я узнал, что настройка SQLALCHEMY_POOL_RECYCLE может регулировать время для flask для повторного использования просроченных соединений. Я попробовал его установить, но мою проблему это не решило.
Это моя демонстрация:

import time

import sqlalchemy as sql
from flask import Flask
from flask_sqlalchemy import SQLAlchemy

db = SQLAlchemy()


class TestData(db.Model):
    id = sql.Column(sql.BigInteger, primary_key=True)

def create_app() -> Flask:
    app: Flask = Flask(__name__)
    app.config["SQLALCHEMY_DATABASE_URI"] = "mysql+pymysql://mysql_host"
    app.config["SQLALCHEMY_POOL_SIZE"] = 1
    app.config["SQLALCHEMY_POOL_RECYCLE"] = 3
    db.init_app(app)
    return app


def test():
    app = create_app()
    with app.app_context():
        q1 = TestData.query.get(0)
        print(q1)
        time.sleep(10)
        q2 = TestData.query.get(1)
        print(q2)

test()

Я запустил SET GLOBAL wait_timeout = 8;, чтобы установить время отключения MySQL на 8 с, а также настроил app.config["SQLALCHEMY_POOL_RECYCLE"] = 3. После time.sleep(10) запрос q2 по-прежнему не удался.

версия пакета

SQLAlchemy==1.3.22
Flask_SQLAlchemy==2.4.4
Flask==1.1.2

🤔 А знаете ли вы, что...
Python поддерживает множество парадигм программирования, включая процедурное, объектно-ориентированное и функциональное программирование.


1
52
1

Ответ:

Решено

Из документации по Setting Pool Recycle:

Обратите внимание, что аннулирование происходит только во время извлечения, а не для каких-либо соединений, которые находятся в извлеченном состоянии.

Ваш первый .get() проверяет соединение, начинает транзакцию и выдает запрос SELECT. Поскольку вы не завершаете транзакцию, соединение остается «открытым» (удерживается в извлеченном состоянии), и через 10 секунд бездействия вы пытаетесь использовать то же самое соединение снова.

Исправление состоит в том, чтобы завершить транзакцию после первого .get(), чтобы соединение вернулось в пул. Через 10 секунд ваш следующий .get() проверит соединение и оно будет перезапущено.

        q1 = TestData.query.get(0)
        print(q1)
        db.session.rollback()  # complete the transaction
        time.sleep(10)
        q2 = TestData.query.get(1)
        print(q2)