Django 中数据库连接失败回滚的正确方法

发布于 2025-01-14 05:17:57 字数 1177 浏览 2 评论 0原文

这更多的是一个设计问题。

直到最近,我一直在开发环境中使用 Django 和 SQLite,但现在我已更改为 PostgreSQL 进行生产。我的应用程序是用 Heroku 部署的,几天后我意识到他们对数据库进行了随机维护,并且它在几分钟内就崩溃了。

例如,有一个包含 3 个表的模型,一个 Procedure,每个表都指向一个 ProcedureList,并且一个 ProcedureList 可以有多个表过程。一个 ProcedureUser,它链接 ProcedureList 和用户,并在该 ProcedureList 上为用户设置一些特定变量。最后还有一个 ProcedureState,它将 Procedure 与其特定用户的状态链接起来。

在我的应用程序中,在其中一个视图中,我有一个按以下方式修改数据库的函数:

user = request.user
plist = ProcedureList.objects.get(id=idFromUrl)
procedures = Procedure.objects.filter(ProcedureList=pList)

pUser = ProcedureUser(plist, user, someVariables)
pUser.save()

for procedure in procedures:
    pState = ProcedureState(plist, user, pUser, procedure, otherVariables)    
    pState.save()
    

所以我现在想的是,如果 Heroku 决定在这些 object.save()< 之间进行维护/code> 调用,我们就会遇到问题。稍后对 .save() 的调用将失败并且数据库将被损坏。用户的请求当然会失败,并且无法回滚之前的插入,因为无法与数据库连接。

我的问题是,如果数据库出现故障(由于 Heroku 维护、网络错误或其他原因),我们应该如何正确回滚数据库?我们是否应该创建一个插入列表并等待 DB 再次上升以将其回滚?

我正在使用 Python 3 和 Django 4,但我认为这更多的是一个普遍问题,而不是特定于任何平台。

This is more of a design question than anything else.

Until recently I have been using Django with SQLite in my development environment, but I have now changed to PostgreSQL for production. My app is deployed with Heroku, and after some days I realized that they do random maintenance to the DB and it goes down during a few minutes.

For example, having a model with 3 tables, one Procedure which each of them point to a ProcedureList, and a ProcedureList can have more than one Procedure. A ProcedureUser which links a ProcedureList and a user and sets some specific variables for the user on that ProcedureList. Finally there is a ProcedureState which links a Procedure with its state for an specific user.

On my app, in one of the views I have a function that modifies the DB in the following way:

user = request.user
plist = ProcedureList.objects.get(id=idFromUrl)
procedures = Procedure.objects.filter(ProcedureList=pList)

pUser = ProcedureUser(plist, user, someVariables)
pUser.save()

for procedure in procedures:
    pState = ProcedureState(plist, user, pUser, procedure, otherVariables)    
    pState.save()
    

So what I'm thinking now, is that if Heroku decides to go into maintenance between those object.save() calls, we will have a problem. The later calls to .save() will fail and the DB will be corrupted. The request by the user will of course fail and there will be no way to rollback the previous insertions, because the connection with the DB is not possible.

My question is, in case of a DB fail (given by Heroku maintenance, network error or whatever), how are we supposed to correctly rollback the DB? Shall we make a list of insertions and wait for DB to go up again to roll them back?

I am using Python 3 and Django 4 but I think this is more of a general question than specific to any platform.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

暮年 2025-01-21 05:17:57

如果数据库出现故障(由于 Heroku 维护、网络错误或其他原因),我们应该如何正确回滚数据库?

数据库通过原子事务[wiki]原子事务是一组提交全部的查询。因此,对于此类事务,不可能应用某些查询而另一些则不应用。

Django 提供了 transaction < em>上下文管理器 [Django-doc]在事务中执行工作:

from django.db import transaction

with transaction.atomic():
    user = request.user
    plist = ProcedureList.objects.get(id=idFromUrl)
    procedures = Procedure.objects.filter(ProcedureList=pList)
    
    pUser = ProcedureUser(plist, user, someVariables)
    pUser.save()
    
    ProcedureState.objects.bulk_create([
        ProcedureState(plist, user, pUser, procedure, otherVariables)
        for procedure in procedures
    ])

在上下文块的末尾,它将提交更改。这意味着如果数据库在其间发生故障,则不会提交操作,并且该块将引发异常(通常是 IntegrityError)。


注意:Django 有一个 .bulk_create(…) 方法[Django-doc] 用于创建多个 项目一个单一数据库查询,最大限度地减少数据库和应用层之间的带宽。这通常会优于在循环中创建项目。

in case of a DB fail (given by Heroku maintenance, network error or whatever), how are we supposed to correctly rollback the DB?

This is solved by databases through atomic transactions [wiki]. An atomic transaction is a set of queries that are committed all or none. It is thus not possible that for such transaction, certain queries are applied whereas others are not.

Django offers a transaction context manager [Django-doc] to perform work in a transaction:

from django.db import transaction

with transaction.atomic():
    user = request.user
    plist = ProcedureList.objects.get(id=idFromUrl)
    procedures = Procedure.objects.filter(ProcedureList=pList)
    
    pUser = ProcedureUser(plist, user, someVariables)
    pUser.save()
    
    ProcedureState.objects.bulk_create([
        ProcedureState(plist, user, pUser, procedure, otherVariables)
        for procedure in procedures
    ])

At the end of the context block, it will commit the changes. This means that if the database fails in between, the actions will not be committed, and the block will raise an exception (usually an IntegrityError).


Note: Django has a .bulk_create(…) method [Django-doc] to create multiple items with a single database query, minimizing the bandwidth between the database and the application layer. This will usually outperform creating items in a loop.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文