【问题标题】:Flask and Celery on Heroku: sqlalchemy.exc.DatabaseError: (psycopg2.DatabaseError) SSL error: decryption failed or bad record macHeroku 上的 Flask 和 Celery:sqlalchemy.exc.DatabaseError: (psycopg2.DatabaseError) SSL 错误:解密失败或坏记录 mac
【发布时间】:2017-12-26 04:52:27
【问题描述】:

我正在尝试在 Heroku 上部署一个烧瓶应用程序,该应用程序使用 Celery 中的后台任务。我已经实现了application factory pattern,这样芹菜进程就不会绑定到烧瓶应用程序的任何一个实例。

这在本地有效,我还没有看到错误。但是当部署到 heroku 时,总是会出现相同的结果:celery 任务(我只使用一个)在第一次运行时成功,但随后对该任务的任何 celery 调用都会失败,并显示sqlalchemy.exc.DatabaseError: (psycopg2.DatabaseError) SSL error: decryption failed or bad record mac。如果我重新启动 celery worker,循环将继续。

multipleissues 显示相同的error,但没有一个指定正确的解决方案。我最初认为实现应用程序工厂模式可以防止出现此错误,但它并不完全存在。

app/__init__.py 我创建了 celery 和 db 对象:

celery = Celery(__name__, broker=Config.CELERY_BROKER_URL)
db = SQLAlchemy()

def create_app(config_name):
    app = Flask(__name__)
    app.config.from_object(config[config_name])

    db.init_app(app)
    return app

我的flask_celery.py 文件创建了实际的 Flask 应用程序对象:

import os
from app import celery, create_app

app = create_app(os.getenv('FLASK_CONFIG', 'default'))
app.app_context().push()

然后我用这个命令启动 celery: celery worker -A app.flask_celery.celery --loglevel=info

这是实际的 celery 任务的样子:

@celery.task()
def task_process_stuff(stuff_id):
    stuff = Stuff.query.get(stuff_id)
    stuff.processed = True
    db.session.add(stuff)
    db.session.commit()
    return stuff

调用者:

task_process_stuff.apply_async(args=[stuff.id], countdown=10)

库版本

  • 烧瓶 0.12.2
  • SQLAlchemy 1.1.11
  • Flask-SQLAlchemy 2.2
  • 芹菜 4.0.2

【问题讨论】:

  • 只是为了确保您不能对数据库进行任何正确的查询?
  • @skagra_dragneel 第一次,我可以连接到数据库并且任务工作正常。之后的任何时候,任务都会立即失败,并且无法对数据库进行查询。
  • 该死的以为我有解决方案,遇到了类似的问题,但我根本无法连接到数据库
  • 您的 celery 工作人员不断重复使用相同的连接。提交会话后db.session.remove() 怎么样?它应该强制重新建立连接。
  • @OluwafemiSule 我尝试在提交后添加db.session.remove(),但错误仍然存​​在。

标签: python heroku flask celery flask-sqlalchemy


【解决方案1】:

解决方案是在任务开始时添加db.engine.dispose(),在任何工作开始之前处理所有数据库连接:

@celery.task()
def task_process_stuff(stuff_id):
    db.engine.dispose()
    stuff = Stuff.query.get(stuff_id)
    stuff.processed = True
    db.session.commit()
    return stuff

由于我的所有任务都需要此功能,因此我将其添加到 task_prerun

@task_prerun.connect
def on_task_init(*args, **kwargs):
    db.engine.dispose()

【讨论】:

  • 谁能解释这个问题的根本原因?为什么这会影响 Celery 工人而不是 Gunicorn 工人?
  • 嗨,我遇到了同样的问题,但我找不到为什么会这样?这是芹菜工人的一种非常奇怪的行为......
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-07-05
  • 2014-05-10
  • 1970-01-01
  • 2021-03-30
  • 2015-06-13
  • 2021-11-19
相关资源
最近更新 更多