Flask-SQLAlchemy:在回滚无效事务之前无法重新连接

Posted

技术标签:

【中文标题】Flask-SQLAlchemy:在回滚无效事务之前无法重新连接【英文标题】:Flask-SQLAlchemy: Can't reconnect until invalid transaction is rolled back 【发布时间】:2015-09-16 06:52:23 【问题描述】:

所以我使用 Amazon Web Services RDS 来运行 mysql 服务器,并使用 Python 的 Flask 框架来运行应用程序服务器,并使用 Flask-SQLAlchemy 来与 RDS 交互。

我的应用配置.py

SQLALCHEMY_DATABASE_URI = '<RDS Host>'
SQLALCHEMY_POOL_RECYCLE = 60

我的 __ 初始化 __.py

from flask import Flask
from flask.ext.sqlalchemy import SQLAlchemy

application = Flask(__name__)
application.config.from_object('config')
db = SQLAlchemy(application)

我有我的主应用程序.py

from flask import Flask
from application import db
import flask.ext.restless
from application.models import Person

application = Flask(__name__)
application.debug=True
db.init_app(application)

@application.route('/')
def index():
    return "Hello, World!"

manager = flask.ext.restless.APIManager(application, flask_sqlalchemy_db=db)
manager.create_api(Person, methods=['GET','POST', 'DELETE'])

if __name__ == '__main__':
    application.run(host='0.0.0.0')

models.py

class Person(db.Model):
    __bind_key__= 'people'
    id = db.Column(db.Integer, primary_key=True)
    firstName = db.Column(db.String(80))
    lastName = db.Column(db.String(80))
    email = db.Column(db.String(80))

    def __init__(self, firstName=None, lastName=None, email=None):
        self.firstName = firstName
        self.lastName = lastName
        self.email = email

然后我有一个脚本来填充数据库,以便在创建数据库和启动应用程序之后进行测试:

from application import db
from application.models import Person

person = Person('Bob', 'Jones', 'bob@website.net')
db.session.add(person)
db.session.commit()

使用 db.drop_all() 和 db.create_all() 重置数据库后,我会启动 application.py,然后启动脚本以填充数据库。

服务器将以正确的 JSON 响应,但如果我在几个小时后回来检查它,我会收到需要回滚的错误,或者有时会收到 MySQL 服务器已消失的 2006 错误。

人们建议我更改 MySQL 服务器上的超时设置,但这并没有解决任何问题。这是我的设置:

innodb_lock_wait_timeout = 3000
max_allowed_packet       = 65536
net_write_timeout        = 300
wait_timeout             = 300

然后,当我查看 RDS 监视器时,它显示 MySQL 服务器保持连接打开了很长一段时间,直到超时。如果我错了,现在纠正我,但连接完成后不应该关闭吗?应用服务器似乎一直在确保数据库连接存在,然后当 MySQL 服务器超时时,Flask/Flask-SQLAlchemy 会抛出错误并随之关闭应用服务器。

任何建议都非常感谢,谢谢!

【问题讨论】:

【参考方案1】:

这似乎不是事务的问题,但这可能是由 MySQL 错误引起的,如 Connection reset by peer 事先。这意味着您的连接丢失,可能是因为您的应用程序上下文设置不正确。

一般来说,最好使用factory pattern 来创建您的应用程序。这有很多优点,你的代码是

更易于阅读和设置 更容易测试 避免循环导入

为防止无效事务错误(可能由OperationalError: Connection reset by peer 引起),您应确保正确处理数据库连接。

以下示例基于this article,它很好地解释了烧瓶应用程序上下文以及如何将其与数据库连接或任何其他扩展一起使用。

应用程序.py


from flask import Flask
from flask_sqlalchemy import SQLAlchemy

def create_app():
    """Construct the core application."""
    application = Flask(__name__)
    application.config.from_object('config')    # Set globals
    db = SQLAlchemy()

    with application.app_context():
        # Initialize globals/extensions in app context
        db.init_app(app)        

        # import routes here
        from . import routes

    return application


if __name__ == "__main__":
   app = create_app()
   app.run(host="0.0.0.0")

routes.py

from flask import current_app as application

@application.route('/', methods=['GET'])
def index():
   return "Hello, World!"

如果您仍然遇到断开连接问题,您还应该查看dealing with disconnects 上的 SQLAlchemy 文档并查看 this question。

【讨论】:

【参考方案2】:

当您将 sqlalchemy 引擎创建为单例时,通常会出现此错误。在这种情况下,连接失效后(在我的情况下是 3600 秒),您会收到 InvalidTransaction 错误。

最好的建议是在应用程序初始化时初始化数据库会话

db.init_app(app)

并在您必须执行一些 CRUD 操作时导入此数据库会话。

在我的应用程序上发布此更改后,从未遇到过此问题。

【讨论】:

这正是我的设置,我创建了一个单例类,我的数据库使用create_engine(..) 初始化。你能解释一下为什么会这样吗?这对我来说没有多大意义!【参考方案3】:

这里您缺少池回收,因为 MySql 在一段时间后关闭会话,因此您需要添加池回收,以便池中的连接在池回收时间后重新连接。

app.config['SQLALCHEMY_POOL_RECYCLE'] = 3600

【讨论】:

【参考方案4】:

每次检查回滚与否都很麻烦..

我做了需要提交的插入、更新函数。

@app.teardown_request
def session_clear(exception=None):
    Session.remove()
    if exception and Session.is_active:
        Session.rollback()

【讨论】:

Flask-SQLAlchemy does 给你。 哦..谢谢!!。我不知道,但我不使用 flask-sqlalchemy 来减少包依赖 @leejaycoke 我使用原始 sqlalchemy 并且遇到问题 Flask-Admin 在长时间后达到需要回滚的状态。我有一个Session.remove() 拆解,但明天会尝试添加这个异常部分 @leejaycoke 这个问题专门询问 Flask-SQLAlchemy。如果不相关,您应该删除您的答案。【参考方案5】:

或者,在填充数据库的脚本末尾使用它:

db.session.close()

这应该可以防止那些烦人的“MySQL 服务器已消失”错误。

【讨论】:

我已经尝试过了,但它没有帮助,我认为它不起作用,因为 Flask-SQLAlchemy(而不仅仅是 SQLAlchemy)已经为你关闭了连接。我只是正确地初始化它。自从我调用 init_app 以来就没有出现过错误。不过,感谢您的反馈! 这可能会帮助以后的人!?我不得不使用with db.session.no_autoflush: 来修复 Flask 中的一些奇怪问题。结果,我的 PaaS (Heroku) 陷入了困境。通过在“with”语句之前放置db.session.close() 可以解决问题。没有恋爱,但一切正常。【参考方案6】:

我认为它添加了什么

db.init_app(application)

在 application.py 中,从那以后就没有错误了。

【讨论】:

他在这里:db = SQLAlchemy(application)。你所做的是重新初始化。 @KanstantsinKamkou 是的,但我有相同的设置,这个额外的初始化也为我修复了它。看看它为什么会修复它会很有趣。 @LeonOverweel 在原始帖子的情况下,它有帮助的原因是因为他在第 6 行用 Flask(name) 的新实例覆盖了他的应用程序定义应用程序.py 我关闭了我的应用程序的进程并重新启动了应用程序。它和平地工作! 我已经将此行添加到我的服务器,但它仍然有同样的问题。谁能帮帮我?

以上是关于Flask-SQLAlchemy:在回滚无效事务之前无法重新连接的主要内容,如果未能解决你的问题,请参考以下文章

flask-sqlalchemy事务

MySQL事务回滚后自增键不连续

Spring data jpa嵌套事务回滚不删除插入?

Laravel 4.2 - 多个数据库的事务回滚问题

sql在具有回滚的事务中多次插入

一个大事务,有很多更新,现在被回滚了,但是又着急关机重启,怎么办才好?