我正在尝试实现(我认为)一个非常简单的计数器数据模型:
class VisitorDayTypeCounter(models.Model):
visitType = models.CharField(max_length=60)
visitDate = models.DateField('Visit Date')
counter = models.IntegerField()
Run Code Online (Sandbox Code Playgroud)
当有人通过时,它将查找与visitType和visitDate匹配的行; 如果此行不存在,则将使用counter = 0创建.
然后我们增加计数器并保存.
我担心的是这个过程完全是一场竞赛.两个请求可以同时检查实体是否存在,并且它们都可以创建它.在读取计数器并保存结果之间,另一个请求可能会通过并递增(导致计数丢失).
到目前为止,我还没有找到一个很好的方法,无论是在Django文档还是在教程中(实际上,看起来教程在投票部分有竞争条件).
我该如何安全地做到这一点?
我试图找到一种方法来阻止用户双重提交我的表单.我有javascript禁用提交按钮,但仍然有偶尔的用户找到双重提交的方法.
我有一个可重用的库的愿景,我可以创建以防止这种情况.
在我理想的库中,代码块看起来像这样:
try:
with acquire_lock({'field1':'abc', 'field2':'def'}) as lock:
response = #do some credit card processing
lock.response = response
except SubmissionWasDuplicate, e:
response = e.response
Run Code Online (Sandbox Code Playgroud)
锁表看起来像这样:
duplicate_submission_locks
有谁知道这是否已经存在?它似乎并不难写,所以如果它不存在我可以自己写.
如何在Django模型中处理并发?我不希望记录的更改被另一个读取相同记录的用户覆盖.
我想拥有用户编辑的大文本字段的完整历史记录,使用Django存储.
我见过这些项目:
我有一个特殊的用例,可能超出了这些项目提供的范围.此外,我对这些项目的文档记录,测试和更新情况保持警惕.无论如何,这是我面临的问题:
我有一个模特,喜欢:
from django.db import models
class Document(models.Model):
text_field = models.TextField()
Run Code Online (Sandbox Code Playgroud)
这个文本字段可能很大 - 超过40k - 我希望有一个自动保存功能,每30秒左右保存一次字段.这可能会使数据库变得非常大,显然,如果每次40k都有很多保存(如果压缩可能仍然是10k).我能想到的最佳解决方案是保持最新保存版本和新版本之间的差异.
但是,我担心涉及并行更新的竞争条件.有两种截然不同的竞争条件(第二种情况比第一种情况严重得多):
HTTP事务竞争条件:用户A和用户B请求文档X0,并单独进行更改,生成Xa和Xb.Xa被保存,X0和Xa之间的差异是"Xa-0"("少了一点"),Xa现在被存储为数据库中的正式版本.如果Xb随后保存,则覆盖Xa,差异为Xb-a("b减去a").
虽然不理想,但我并不过分担心这种行为.文档相互重写,用户A和B可能彼此不知道(每个文档都以文档X0开头),但历史保留了完整性.
数据库读取/更新竞争条件:有问题的竞争条件是Xa和Xb同时保存在X0上.会有(伪)代码类似于:
def save_history(orig_doc, new_doc):
text_field_diff = diff(orig_doc.text_field, new_doc.text_field)
save_diff(text_field_diff)
Run Code Online (Sandbox Code Playgroud)
如果Xa和Xb都从数据库中读取X0(即orig_doc是X0),它们的差异将变为Xa-0和Xb-0(与序列化的Xa-0相反,然后是Xb-a,或等效的Xb-0然后Xa b).当您尝试将差异拼接在一起以生成历史记录时,它将在修补程序Xa-0或Xb-0(均适用于X0)上失败.历史的完整性已经受到损害(或者有吗?).
一种可能的解决方案是自动协调算法,该算法可以事后检测这些问题.如果重建历史记录失败,可能会假设已发生竞争条件,因此将失败的修补程序应用于历史记录的先前版本,直到成功为止.
我很高兴能就如何解决这个问题得到一些反馈和建议.
顺便说一句,只要它是一个有用的出路,我注意到这里讨论了Django原子性:
非常感谢你.
我有 2 个模型:Product和Order.
Product有一个股票的整数字段,而Order有一个状态和一个外键Product:
class Product(models.Model):
name = models.CharField(max_length=30)
stock = models.PositiveSmallIntegerField(default=1)
class Order(models.Model):
product = models.ForeignKey('Product')
DRAFT = 'DR'; INPROGRESS = 'PR'; ABORTED = 'AB'
STATUS = ((INPROGRESS, 'In progress'),(ABORTED, 'Aborted'),)
status = models.CharField(max_length = 2, choices = STATUS, default = DRAFT)
Run Code Online (Sandbox Code Playgroud)
我的目标是让每个新订单的产品库存减少一个,每个订单取消增加一个。为此,我重载了模型的save方法Order(受Django启发:保存时,如何检查字段是否已更改?):
from django.db.models import F
class Order(models.Model):
product = models.ForeignKey('Product')
status = models.CharField(max_length = 2, choices = STATUS, default …Run Code Online (Sandbox Code Playgroud) 我有一个模型MyModel,它有一个字段expiration_datetime.
每次用户检索MyModel的实例时,我都需要先检查它是否已过期.如果它已经过期,我需要增加一些计数器,更新其他计数器,然后将expiration_datetime扩展到将来的某个时间.
所以视图会做类似的事情:
if object.expiration_datetime < datetime.datetime.now():
object.counter = F('counter') + 1
object.expiration_datetime = F('expiration_datetime') + datetime.timedelta(days=1)
object.save()
Run Code Online (Sandbox Code Playgroud)
上面的代码中存在竞争条件.假设线程1检查并发现当前实例已过期,它继续增加计数器并延长到期日期时间.但是在它可以这样做之前,线程2被安排并且做同样的事情.到线程1最终完成时,计数器已递增两次,expration_datetime已延长两次.
这看起来应该是一个非常常见的问题.处理它的最有效方法是什么?理想情况下,我希望能够以数据库可移植的方式在Django中处理它.
django ×6
concurrency ×3
python ×3
database ×2
atomic ×1
code-reuse ×1
locking ×1
mysql ×1
postgresql ×1
transactions ×1