简体   繁体   English

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

[英]Proper way to rollback on DB connection fail in Django

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.直到最近,我一直在我的开发环境中使用 Django 和 SQLite,但我现在已更改为生产环境中的 PostgreSQL。 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.我的应用程序是使用 Heroku 部署的,几天后我意识到他们对数据库进行随机维护并且它会在几分钟内关闭。

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 .例如,有一个 model 和 3 个表,一个Procedure每个指向一个ProcedureList ,一个ProcedureList可以有多个Procedure A ProcedureUser which links a ProcedureList and a user and sets some specific variables for the user on that ProcedureList .一个ProcedureUser ,它链接一个ProcedureList和一个用户,并为该ProcedureList上的用户设置一些特定的变量。 Finally there is a ProcedureState which links a Procedure with its state for an specific user.最后有一个ProcedureState ,它为特定用户链接一个Procedure及其 state。

On my app, in one of the views I have a function that modifies the DB in the following way:在我的应用程序中,在其中一个视图中,我有一个 function 以下列方式修改数据库:

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.所以我现在想的是,如果 Heroku 决定在object.save()调用之间对 go 进行维护,我们就会遇到问题。 The later calls to .save() will fail and the DB will be corrupted.稍后对.save()的调用将失败,并且数据库将被损坏。 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.用户的请求当然会失败,并且没有办法回滚之前的插入,因为无法与 DB 连接。

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?我的问题是,如果数据库出现故障(由 Heroku 维护、.network 错误或其他原因给出),我们应该如何正确回滚数据库? Shall we make a list of insertions and wait for DB to go up again to roll them back?我们是否应该制作一个插入列表并等待 DB 再次上升到 go 以将它们回滚?

I am using Python 3 and Django 4 but I think this is more of a general question than specific to any platform.我正在使用 Python 3 和 Django 4 但我认为这是一个普遍的问题,而不是特定于任何平台的问题。

in case of a DB fail (given by Heroku maintenance,.network error or whatever), how are we supposed to correctly rollback the DB?如果数据库出现故障(由 Heroku 维护、.network 错误或其他原因给出),我们应该如何正确回滚数据库?

This is solved by databases through atomic transactions [wiki] .这是由数据库通过原子事务[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: Django 提供了一个transaction上下文管理器[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
    ])

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 ).这意味着如果数据库在此期间发生故障,则不会提交操作,并且该块将引发异常(通常是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.注意:Django 有一个.bulk_create(…)方法[Django-doc]可以通过单个数据库查询创建多个项目,从而最大限度地减少数据库和应用层之间的带宽。 This will usually outperform creating items in a loop.这通常会优于在循环中创建项目。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM