我们应该如何制作长期交易"完整性错误"使用Django时免费? (特别是外键完整性错误)
我们的应用程序有实体' A'和' B' (为了简单起见) 它具有以下语义。
' B'有一个外键字段指向' A'。
定期,多个' B' (在成千上万的中)在事件发生时创建。
创建多个B&B应该始终是事务性的,因为我们应该始终构建所有的B或者什么都没有,以便在出现错误的情况下,我们可以重新触发事件。
间歇性的,一些' A'可能会被摧毁。换句话说,我们无法控制A'
我目前正在使用django的基本交易功能(transaction.atomic)来确保B的原子创作行为。
由于建筑物需要花费一些时间(大约1小时),如果已经引用了一些' B' ' A'在此期间被删除,整个事务因ForeignKey完整性错误而失败。
为了清楚起见,如果我可以用粗略的图解释。
按顺序创建Bi,
[B1(参考文献A1),B2(参考文献A2)....... Bn(参考文献)]仅在创建了所有B1 ... Bn时尝试进行整个操作。
创建Bm(1&lt; = m&lt; n)时,Ak(1 <= k 如果控制&#39; A&#39;如此简单,那么这不会成为一个问题。
如果构建多个B的时间太短,那么这不会成为问题。 自建筑&#39; B&#39;花费这么多时间,只有在我们发现完整性错误在生产环境中不能正常工作之后重新尝试。 在最好的情况下,如果完整性错误被抑制并且错误的行被静默删除,那么引用已被删除的行&#39; A&#39;。 我目前正在使用带有Postgre的Django。
任何评论都非常感谢。 另一项要求是锁定表&#34; A&#34; (无论是表级还是行级)不应该被使用,因为查询,修改和插入(删除除外)操作应该是可用的,无论我是否构建&#34; B&#34;或不。 &#34; A&#34;是一个用户信息表,随时可以更改(我们可以在需要时推迟删除)
答案 0 :(得分:1)
如果某些已经引用过的&#39; &#39; A&#39;在此期间被删除,整个事务因ForeignKey完整性错误而失败。
你需要防止这种情况发生。最好的方法是采取适当的锁定&#34; A&#34;在开始工作之前的对象。
我建议,在构建&#34; B&#34;的事务中,首先是SELECT 1 FROM "A" WHERE ... FOR SHARE
,或者使用任何Django级抽象来执行相同的基础SELECT ... FOR SHARE
行被引用。
SELECT ... FOR UPDATE
也没关系,只需要一个你不需要的更强锁。
Django可能称之为&#34;显式&#34;或者&#34;悲观&#34;锁定。 (谷歌)。烨:
select_for_update
in the Django docs。它似乎不支持FOR SHARE
,但我想这是您在使用ORM时必须遵守的限制。如果多余的东西需要防止相同的情况,那只会是一个问题。&#34; A&#34;同时删除对象,因为FOR SHARE
它们可以同时进行,而FOR UPDATE
时,它们必须相互串行等待。
-
在您的评论澄清之后:听起来您真正想要的是a feature added in PostgreSQL 9.3, FOR KEY SHARE
,它只锁定引用值的主键,因此您仍然可以更改其他列。当然,您当然必须使用本机SQL来执行此操作,并且它是SQL标准FOR SHARE
和FOR UPDATE
的PostgreSQL特定扩展。
答案 1 :(得分:0)
由于构建'B'需要一些时间(1小时左右),如果在此期间某些'已引用的''A'被删除,整个事务将因ForeignKey完整性错误而失败。
如何使用pre_delete信号?您需要检查B的交易是否正在完成。如果是这种情况,请在信号中引发错误,如果不是,只需删除A对象。
PS:我不知道这是否会起作用,这只是一个想到这个问题的想法。希望它会!编辑:进一步说明
class B(models.Model): transaction_active = False def mylongtransactionview(request): models.B.transaction_active = True go_long_transaction() models.B.transaction_active = False def mypre_delete_A_signal(): if models.B.transaction_active: raise Exception else: pass