【问题标题】:flask_sqlalchemy `pool_pre_ping` only working sometimes烧瓶 sqlalchemy `pool_pre_ping` 有时只工作
【发布时间】:2019-11-14 21:10:26
【问题描述】:

为了测试,我将MYSQL(RDS)参数修改如下;

wait_timeout = 40(默认为 28800)

max_allowed_pa​​cket = 1GB(最大值 - 只是为了确保问题不是由小数据包引起的)

net_read_timeout = 10

interactive_timeout 不变

然后在没有设置pool_pre_ping 选项的情况下测试我的应用程序(默认为 False),让应用程序保持非活动状态 40 秒,尝试登录,然后我得到了

Nov 14 20:05:20 ip-172-31-33-52 gunicorn[16962]: Traceback (most recent call last):
Nov 14 20:05:20 ip-172-31-33-52 gunicorn[16962]:   File "/var/www/api_server/venv/lib/python3.6/site-packages/sqlalchemy/engine/base.py", line 1193, in _execute_context
Nov 14 20:05:20 ip-172-31-33-52 gunicorn[16962]:     context)
Nov 14 20:05:20 ip-172-31-33-52 gunicorn[16962]:   File "/var/www/api_server/venv/lib/python3.6/site-packages/sqlalchemy/engine/default.py", line 507, in do_execute
Nov 14 20:05:20 ip-172-31-33-52 gunicorn[16962]:     cursor.execute(statement, parameters)
Nov 14 20:05:20 ip-172-31-33-52 gunicorn[16962]:   File "/var/www/api_server/venv/lib/python3.6/site-packages/MySQLdb/cursors.py", line 206, in execute
Nov 14 20:05:20 ip-172-31-33-52 gunicorn[16962]:     res = self._query(query)
Nov 14 20:05:20 ip-172-31-33-52 gunicorn[16962]:   File "/var/www/api_server/venv/lib/python3.6/site-packages/MySQLdb/cursors.py", line 312, in _query
Nov 14 20:05:20 ip-172-31-33-52 gunicorn[16962]:     db.query(q)
Nov 14 20:05:20 ip-172-31-33-52 gunicorn[16962]:   File "/var/www/api_server/venv/lib/python3.6/site-packages/MySQLdb/connections.py", line 224, in query
Nov 14 20:05:20 ip-172-31-33-52 gunicorn[16962]:     _mysql.connection.query(self, query)
Nov 14 20:05:20 ip-172-31-33-52 gunicorn[16962]: MySQLdb._exceptions.OperationalError: (2013, 'Lost connection to MySQL server during query')

像这样添加pool_pre_ping(使用flask_sqlalchamy 2.4.1版);

import os
from flask import Flask
from flask_sqlalchemy import SQLAlchemy as _BaseSQLAlchemy


class SQLAlchemy(_BaseSQLAlchemy):
    def apply_pool_defaults(self, app, options):
        super(SQLAlchemy, self).apply_pool_defaults(app, options)
        options["pool_pre_ping"] = True
#        options["pool_recycle"] = 30
#        options["pool_timeout"] = 35

db = SQLAlchemy()


class DevConfig():
    SQLALCHEMY_ENGINE_OPTIONS = {'pool_recycle': 280, 'pool_timeout': 100, 'pool_pre_ping': True} # These configs doesn't get applied in engine configs :/
    DEBUG = True
    # SERVER_NAME = '127.0.0.1:5000'
    SQLALCHEMY_DATABASE_URI = os.getenv('SQLALCHEMY_DATABASE_URI_DEV')
    SQLALCHEMY_TRACK_MODIFICATIONS = False

config = dict(
    dev=DevConfig,
)

app = Flask(__name__, instance_relative_config=True)
app.config.from_object(config['dev'])

# INIT DATABASE
db.init_app(app)
with app.app_context():
    db.create_all()

-----------run.py
app.run(host='127.0.0.1', port=5000)

有了这个,现在 webapp 可以在 MySQL 服务器关闭之前的连接之后获得新的连接。当我在服务器关闭数据库后立即访问数据库时它总是工作正常(最多尝试 50 秒后)......但是当我长时间保持连接不活动时(没有注意到,但 ~ >10-15 分钟),再次我看到同样的错误。
根据文档(尤其是Dealing with disconnects 部分),pool_pre_ping 选项应该在后台仪式中处理这种情况吗?或者我需要在 MySQL 服务器中更改任何其他超时变量吗?

【问题讨论】:

  • 你的 MySQL 连接器是什么?
  • 忽略连接和池问题,您的程序应该运行多长时间?小时?天?它会闲置几个小时吗?或者至少不接触 MySQL,但希望连接保持完整?你有任何“交易”吗?您是否完全使用autocommit=ON
  • 这是一个后端服务器,需要 24/7 运行。是的,现在我们预计会有数小时和数天的空闲时间。我认为每次 mysql 连接后都没有任何未关闭的事务。如何检查 autocommit=ON ?
  • 嗨@AnumSheraz,你找到解决方案了吗?我遇到了同样的问题。我尝试过使用 SQLALCHEMY_POOL_RECYCLE = 45 和 SQLALCHEMY_ENGINE_OPTIONS = {'pool_pre_ping': True},但没有成功。我从 flask_sqlalchemy (def create_engine) 调试了 init 文件,似乎这个处理 sqlalchemy.create_engine 与所有自定义配置,但仍然不知道为什么不起作用。任何帮助都会很棒。问候。附言我正在使用 Flask-SQLAlchemy==2.4.1
  • 这里一样,无法弄清楚如何重新连接到数据库,但在 PostgreSQL 上,同样的问题,10-20 分钟后,得到OperationalError 并且无法重新连接。尝试为此编写一个测试以在连接丢失时模拟OperationalError,但由于某种原因无法手动执行。很高兴知道如何使用 flask-sqlalchemy 来做到这一点。

标签: python mysql flask flask-sqlalchemy connection-pooling


【解决方案1】:

来自 Flask-SQLAlchemy Configuration docs

某些数据库后端可能会施加不同的非活动连接 超时,这会干扰 Flask-SQLAlchemy 的连接池。

默认情况下,MariaDB 配置为有 600 秒的超时。这个 经常表面难以调试,生产环境只有异常 喜欢

2013: Lost connection to MySQL server during query.

如果您使用的是后端(或预配置的数据库即服务) 具有较低的连接超时,建议您设置 SQLALCHEMY_POOL_RECYCLE 设置为小于后端超时时间的值。

问题中引用的脚本显示其 MySQL timeout-configs (wait_timeout, net_read_timeout) 与其 SQLAlchemy (pool_recycle, pool_timeout) 和 Flask-SQLAlchemy @987654327 之间存在差异@超时(SQLALCHEMY_POOL_RECYCLESQLALCHEMY_POOL_TIMEOUT)。

我们可以通过使用DevConfig 辅助类来协调整个应用程序的数据库连接配置常量来解决这个问题。为此,我们将配置分配给静态属性并重新引用它们,这样就不会有冲突的超时预期。这是一个实现:

import os
from flask import Flask
from flask_sqlalchemy import SQLAlchemy as _BaseSQLAlchemy

# Coordinate DevConfig with SQLAlchemy and Flask-SQLAlchemy (don't repeat yourself!)

class DevConfig():
    SQLALCHEMY_POOL_RECYCLE = 35  # value less than backend’s timeout
    SQLALCHEMY_POOL_TIMEOUT = 7  # value less than backend’s timeout
    SQLALCHEMY_PRE_PING = True
    SQLALCHEMY_ENGINE_OPTIONS = {'pool_recycle': SQLALCHEMY_POOL_RECYCLE, 'pool_timeout': SQLALCHEMY_POOL_TIMEOUT, 'pool_pre_ping': SQLALCHEMY_PRE_PING}
    DEBUG = True
    # SERVER_NAME = '127.0.0.1:5000'
    SQLALCHEMY_DATABASE_URI = os.getenv('SQLALCHEMY_DATABASE_URI_DEV')
    SQLALCHEMY_TRACK_MODIFICATIONS = False

class SQLAlchemy(_BaseSQLAlchemy):
    def apply_pool_defaults(self, app, options):
        super(SQLAlchemy, self).apply_pool_defaults(app, options)
        options["pool_pre_ping"] = DevConfig.SQLALCHEMY_PRE_PING
#        options["pool_recycle"] = 30
#        options["pool_timeout"] = 35

db = SQLAlchemy()

config = dict(
    dev=DevConfig,
)

app = Flask(__name__, instance_relative_config=True)
app.config.from_object(config['dev'])

# INIT DATABASE
db.init_app(app)
with app.app_context():
    db.create_all()

如果您愿意,可以查看the diff 了解我所做的更改:diffchecker.com/Q1e85Hhc

【讨论】:

    【解决方案2】:

    我设置了以下设置:

    SQLALCHEMY_ENGINE_OPTIONS = {
        'pool_size': 10,
        'pool_recycle': 60,
        'pool_pre_ping': True
    }
    

    过去几个月已经停止下降......

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-12-29
      • 2014-07-20
      • 2020-05-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-10-24
      相关资源
      最近更新 更多