在 Django Web 应用程序中,我们需要允许用户通过一系列 GET/POST 请求构建一组更改,然后在最后一次 POST 中提交这些更改到数据库或撤销这些更改。我们需要在提交之前使这些更新与任何并发数据库用户隔离,因为这是一个配置前端,不允许在每次 POST 之后提交更改。
理想的解决方案是使用一个会话级的事务。这将把记住更改的内容(以及它如何影响后续查询),以及实现提交/回滚等问题都交由数据库来处理。由于外部约束,任何时候只能有一个用户配置系统,并且他们都是行为良好的,因此死锁和长时间持有锁的问题不存在。
但是,在 Django 的文档中找不到关于如何设置 ORM 以使用这种事务模型的信息。为了解决这个问题,我们拼凑了一个最小的单补丁(ew!),但这种解决方案非常脆弱。我们想知道是否有其他人做过类似的事情,或者我们是否遗漏了某个地方的文档。
(我们的 Django 版本是 1.0.2 Final,我们使用的是 Oracle 数据库。)
2、解决方案
答案 1:
- 使用备忘录模式来记录用户的更改,而不是使用会话级事务。
- 将备忘录对象的序列累积到会话中。
- 在提交更改之前,重放备忘录的序列,确保它们仍然能够正常工作。
- 如果备忘录序列仍然有效,则执行提交操作,将更改应用到数据库并保存结果。
- 如果备忘录序列无效,则有人更改了某些内容,需要返回到步骤 2:现在该怎么办?
这种方法虽然复杂,但不会持有任何锁,允许极快的速度并且几乎没有死锁的机会。
答案 2:
- 提供了一个单补丁来实现会话级事务,但该补丁很脆弱且不美观,它还更改了私有方法。
- 该补丁通过修改 django.db.connection 对象来实现会话级事务。
- 该补丁需要在应用程序的每个方法中设置用户对象,以确保会话级事务能够正常工作。
答案 3:
- 使用了一个类似于备忘录模式但又略有不同的方法来实现会话级事务。
- 当用户开始编辑会话时,将目标对象复制到数据库中的一个临时对象。
- 所有后续编辑操作都影响临时对象。
- 存储操作对象而不是在每次更改时保存对象状态。
- 当将操作应用于对象时,它返回反向操作,然后将其存储起来。
- 存储操作比存储备忘录要便宜得多,因为操作可以用少量的数据项来描述,而正在编辑的对象要大得多。
- 还可以将结果操作推送到重做栈中,以便实现重做功能。
- 要实现“撤销”操作,从栈中弹出最后一个撤消对象(从数据库中检索临时对象的最新操作),并将其应用于临时对象,然后返回转换后的临时对象。
- 要实现“保存更改”,即提交,取消激活并给原始对象加上时间戳,然后激活临时对象并将其放在原始对象的位置。
- 要实现“取消”操作,即回滚,什么都不做!可以删除临时对象,因为一旦编辑会话结束,用户就无法检索它了,但更倾向于保留已取消的编辑会话,以便在使用 cron 作业清除它们之前对它们运行统计数据。
总之,对于在 Django 中使用会话级事务,我们可以选择备忘录模式、单补丁或操作对象模式,具体选择取决于具体的业务场景和需求。