Django 和具有写入实例+多个只读副本的数据库——运行 Celery 作业

Jas*_*enX 7 django django-models

我有一个正在生产中运行的 Django 应用程序。它的数据库有主要的写入实例和一些读取副本。我通常DATABASE_ROUTERS根据是否需要读取或写入在写入实例和读取副本之间进行路由。

我遇到了一种情况,由于用户请求,我必须对对象进行一些异步处理。动作顺序是:

  1. 用户通过 HTTPS/REST 提交请求。
  2. 视图创建一个对象并将其保存到数据库中。
  3. 触发 celery 作业以在请求-响应周期之外处理对象并将对象 ID 传递给它。
  4. 发送对请求的 OK 响应。

现在,celery 作业可能会在 10 毫秒或 10 分钟内启动,具体取决于队列。当它最终启动时,celery 作业首先尝试根据提供的 ID 加载对象。最初,我在执行 a 时遇到了问题my_obj = MyModel.objects.get(pk=given_id),因为此时将使用只读副本,如果队列为空并且 celery 作业在触发后立即运行,则对象可能尚未传播到只读副本。

我通过替换为解决了这个问题my_obj = MyModel.objects.get(pk=given_id)--my_obj = MyModel.objects.using('default').get(pk=given_id)这确保了从我的 write-db-instance 读取对象并且该对象始终可用。

然而,现在我遇到了另一个我没有预料到的问题。

my_obj.certain_many_to_many_objects.all()由于 ORM 是惰性的,调用会触发对数据库的另一个调用。该调用在只读副本上完成的。我希望它会坚持我定义的数据库,using但事实并非如此。有没有办法强制所有子元素对象使用相同的 write-db-instance?

Ste*_*ven 3

我怀疑您的自定义数据库路由器需要调整。默认行为没有应该提供您所需的数据库粘性

\n
\n

默认路由方案确保对象保持\n\xe2\x80\x98粘性\xe2\x80\x99 到其原始数据库(即,从 foo 数据库检索的对象将保存在同一数据库上)。\n[。 ..]\n\xe2\x80\x99 不需要执行任何操作来激活默认路由方案 \xe2\x80\x93\nit 在每个 Django 上都提供了 \xe2\x80\x98 开箱即用 \xe2\x80\x99项目。

\n
\n

自动数据库路由

\n

因此,您的数据库路由器只需要预先提供此行为,这在 99.9% 的情况下可能是正确的做法。

\n
def db_for_read(model, **hints):\n    instance = hints.get(\'instance\')\n    if instance is not None and instance._state.db:\n        return instance._state.db\n    # else return your read replica\n    return \'read-only\'  # or whatever it\'s called\n
Run Code Online (Sandbox Code Playgroud)\n

请参阅django/db/utils.py

\n