Sha*_*hin 17 python database django django-models race-condition
如何在模型的save()方法中处理可能的竞争条件?
例如,以下示例实现具有相关项的有序列表的模型.创建新项目时,当前列表大小用作其位置.
据我所知,如果同时创建多个项目,这可能会出错.
class OrderedList(models.Model):
# ....
@property
def item_count(self):
return self.item_set.count()
class Item(models.Model):
# ...
name = models.CharField(max_length=100)
parent = models.ForeignKey(OrderedList)
position = models.IntegerField()
class Meta:
unique_together = (('parent','position'), ('parent', 'name'))
def save(self, *args, **kwargs):
if not self.id:
# use item count as next position number
self.position = parent.item_count
super(Item, self).save(*args, **kwargs)
Run Code Online (Sandbox Code Playgroud)
我遇到过@transactions.commit_on_success()但这似乎只适用于视图.即使它确实适用于模型方法,我仍然不知道如何正确处理失败的事务.
我当前正在处理它,但它感觉更像是一个黑客而不是一个解决方案
def save(self, *args, **kwargs):
while not self.id:
try:
self.position = self.parent.item_count
super(Item, self).save(*args, **kwargs)
except IntegrityError:
# chill out, then try again
time.sleep(0.5)
Run Code Online (Sandbox Code Playgroud)
有什么建议?
上述解决方案的另一个问题是,while如果IntegrityError由name冲突(或该问题的任何其他唯一字段)引起,则循环将永远不会结束.
为了记录,这是我到目前为止所做的我似乎做的事情:
def save(self, *args, **kwargs):
# for object update, do the usual save
if self.id:
super(Step, self).save(*args, **kwargs)
return
# for object creation, assign a unique position
while not self.id:
try:
self.position = self.parent.item_count
super(Step, self).save(*args, **kwargs)
except IntegrityError:
try:
rival = self.parent.item_set.get(position=self.position)
except ObjectDoesNotExist: # not a conflict on "position"
raise IntegrityError
else:
sleep(random.uniform(0.5, 1)) # chill out, then try again
Run Code Online (Sandbox Code Playgroud)
Ale*_*lli 15
它可能感觉像是一个黑客,但对我来说,它看起来像一个合理的,合理的"乐观并发"方法的实现 - 尝试做任何事情,检测由竞争条件引起的冲突,如果发生,稍后重试.有些数据库系统地使用它而不是锁定,它可以带来更好的性能,除非在大量写入负载下的系统(在现实生活中很少见).
我非常喜欢它,因为我认为这是Hopper原则的一般情况:"很容易请求宽恕而非许可",这在编程中广泛应用(特别是但不仅限于Python) - Hopper通常被认为是毕竟,Cobol ;-).
一个改进我建议是等待一个随机的时间-避免"元竞态条件",其中两个进程试图在同一时间,发现两者的冲突,都重试再次在同一时间,导致"饥饿". time.sleep(random.uniform(0.1, 0.6))等等应该足够了.
更精确的改进是如果更多冲突得到满足则延长预期等待 - 这就是TCP/IP中所谓的"指数退避"(您不必以指数方式延长事物,即每个乘数> 1)当然,时间,但这种方法有很好的数学属性).这只是必要的,以限制对问题非常写加载系统(其中在试图写入多个冲突经常发生),它可能会不值得你的具体情况.
| 归档时间: |
|
| 查看次数: |
3663 次 |
| 最近记录: |