在celery 任务中反映出SQLAlchemy元数据?

内容来源于 Stack Overflow,并遵循CC BY-SA 3.0许可协议进行翻译与使用

  • 回答 (1)
  • 关注 (0)
  • 查看 (32)

为了更好的可测试性和其他原因,最好在下面的问题中非常好地描述SQLAlchemy数据库会话配置非全局:

如何在没有全局变量的芹菜任务中设置sqlalchemy会话(并在https://github.com/celery/celery/issues/3561中讨论)

现在,问题是,如何优雅地处理元数据?如果我的理解是正确的,元数据可以有一次,例如:

engine = create_engine(DB_URL, encoding='utf-8', pool_recycle=3600,
                       pool_size=10)
# db_session = get_session()  # this is old global session
meta = MetaData()
meta.reflect(bind=engine)

反映每个任务执行不利于性能原因,元数据或多或少是稳定的和线程安全的结构(如果我们只读它)。

但是,元数据有时会发生变化(celery不是db模式的“所有者”),从而导致工作者出错。

什么是一种meta以可测试的方式处理的优雅方式,还能够对底层数据库更改做出反应?(使用中的蒸馏器,如果相关的话)。

我正在考虑使用alembic版本更改作为重新反映的信号,但不太确定如何使其在芹菜中很好地工作。例如,如果不止一个工作人员将立刻感知到更改,则meta可以以非线程安全方式处理全局。

如果重要的话,在这种情况下celery使用是独立的,没有网络框架模块/应用程序/芹菜应用程序中的任何内容。问题也得到了简化,因为只使用了SQLAlchemy Core,而不是对象映射器。

提问于
用户回答回答于

这只是部分解决方案,它适用于SQLAlchemy ORM(但我想类似的东西很容易为Core实现)。

要点:

  • 引擎处于模块级别,但config(访问URL,参数)来自 os.environ
  • 会话是在它自己的工厂功能
  • 在模块级别:BaseModel = automap_base()然后表类使用该BaseModel作为超类,通常只使用一个参数 - __tablename__但是可以在那里添加任意关系,属性(非常类似于正常的ORM使用)
  • 在模块级别: BaseModel.prepare(ENGINE, reflect=True)

测试(使用pytest)在模块级别注入环境变量(例如DB_URLconftest.py

一个重要时刻:database_session始终在任务函数中启动(即调用工厂函数),并显式传播到所有函数中。这种方式允许自然地控制工作单元,通常每个任务一个事务。这也简化了测试,因为所有使用数据库的功能都可以提供虚假或真实(测试)数据库会话。

“任务函数”是上面的一个函数,它在函数中调用,它由任务装饰 - 这种方式任务函数可以在没有任务机器的情况下进行测试。

这只是部分解决方案,因为没有重做反射。如果任务工作者可以暂时停止(并且数据库由于架构更改而无论如何都会遇到停机时间),因为这些通常是后台任务,所以它不会造成问题。一些外部监视程序也可以重新启动工作程序,这些监视程序可以监视数据库更改。通过使用supervisord或其他方式来控制在前台运行的芹菜工人,这可以很方便。

总而言之,在我解决了上述问题之后,我更加重视“明确胜过隐性”哲学。所有那些神奇的“应用程序”,“请求”在芹菜或Flask中,可能会在函数签名中带来微小的缩写,但我宁愿在调用链中传递某种上下文以提高可测试性和更好的上下文理解和管理。

扫码关注云+社区

领取腾讯云代金券